Python və Dekoratorlar
Dekoratorlar bir çox Python kitabxanası və freymvörklərində istifadə olunan özəllikdir. Hər hansı metodu istifadə etməzdən qabaq görüləcək işləri özündə ehtiva edir.
Nümunə: Məsələn, Django freymvörkündə hansısa spesifik bir URL-də istifadəçinin öz hesabına daxil olub-olmadığını yoxlayırıq. Gəlin birlikdə öyrənək.
@login_required
def account_view(request):
return HttpResponse('Çox gizli səhifə ;)')
account_view funksiyasının üzərinə yazılan dekorator ora sorğu göndərməzdən
qabaq hesaba daxil olub-olmadığını yoxlayır. Əgər daxil olubsa, cavab qaytarır,
yoxsa login səhifəsinə yönləndirir.
Bəs bunun işləmə prinsipi nədir? Necə olur ki, həmin funksiya çağırılmamışdan
qabaq login_required işə düşür? Əslində işləmə prinsipi çox sadədir. İşin
sehri sadəcə sintaktik şəkərdədir.
İlk addımlar
Fərz edək ki, bir ədədi digərinə bölən funksiyamız var:
def divider(num1, num2):
return num1 / num2
divider(5, 2)
divider(5, 0)
İkinci çağırılmada 5 və 0 ötürdük və 0-a bölmə olmadığından Python bizə
ZeroDivisionError qaytardı. Dekorator vasitəsilə biz çıxa biləcək xətaları
əvvəlcədən idarə edə bilərik.
İlk öncə sadə bir misala baxaq:
def my_decorator(func):
def wrapper():
print("Funksiyadan qabaq işləyəcək.")
# parametr kimi ötürdüyümüz funksiya
func()
print("Funksiyadan sonra işləyəcək.")
return wrapper
def divide():
print("divide funksiyası")
foo = my_decorator(divide)
foo()
Bu kod bizə belə bir nəticə qaytaracaq:
Funksiyadan qabaq işləyəcək.
divide funksiyası
Funksiyadan sonra işləyəcək.
Burada biz my_decorator adında bir funksiya yaratdıq. Onun içində wrapper
adında daxili (inner) funksiya var. Əlavə olaraq divide funksiyası yaratdıq.
Daha sonra foo adında bir dəyişən yaradıb divide() funksiyasını parametr
kimi ötürdüyümüz my_decorator funksiyasına mənimsətdik.
foo() funksiyasını çağıranda:
- İlk öncə
my_decoratorişə düşür. - Ardınca içindəki
wrapper()işləyir. - Ən sonda isə parametr kimi ötürülən
divide()işə düşür.
Göründüyü kimi, çox sadə məntiqlə — bir funksiyanı digərinə ötürməklə — istənilən funksiyadan qabaq hər hansısa bir əməliyyat etmək mümkündür.
Dekoratorlar və arqumentlər
Əgər bizim dekorator ilə istifadə edəcəyimiz funksiyamız (divide) parametr
qəbul edəcəksə, o zaman belə bir xəta ilə qarşılaşa bilərik:
def my_decorator(func):
def wrapper():
print("Funksiyadan qabaq işləyəcək.")
func()
print("Funksiyadan sonra işləyəcək.")
return wrapper
def divide(num1, num2):
print(num1 / num2)
foo = my_decorator(divide)
foo(5, 2)
Nəticə:
Traceback (most recent call last):
File "main.py", line 12, in <module>
foo(5, 2)
TypeError: wrapper() takes 0 positional arguments but 2 were given
Bu xətanı biz ona görə aldıq ki, daxili wrapper içində çağırılan func()
metoduna heç bir arqument verməmişik. Halbuki divide() bizdən num1 və num2
dəyərlərini gözləyir.
Dekoratorlarda daxili funksiyamız olan wrapper(), əsas funksiyamızın
(divide()) parametrlərini qəbul edə bilir. Biz də bunu func()-a ötürə
bilərik:
def my_decorator(func):
def wrapper(num1, num2):
print("Funksiyadan qabaq işləyəcək.")
func(num1, num2)
print("Funksiyadan sonra işləyəcək.")
return wrapper
def divide(num1, num2):
print(num1 / num2)
foo = my_decorator(divide)
foo(5, 2)
Bununla da xəta aradan qalxmış olur.
Əgər funksiya 2 yox, daha çox parametr qəbul etsə, hər dəfə bunları yazmaq o qədər də ağlabatan deyil. Üstəlik, hər funksiya üçün eyni işi görən ayrı-ayrı dekoratorlar yazmalı olacağıq.
Bunun həll yolu *args və **kwargs ikilisidir.
*args— funksiyaya ötürülən istənilən mövqeli (positiona) arqumentləri qəbul edir.**kwargs— istənilən açar sözlü (keyword) arqumentləri qəbul edir.
Bu ikili haqqında daha ətraflı məlumat almaq üçün bu məqaləyə baxa bilərsiniz.
def my_decorator(func):
def wrapper(*args, **kwargs):
print("Funksiyadan qabaq işləyəcək.")
func(*args, **kwargs)
print("Funksiyadan sonra işləyəcək.")
return wrapper
def divide(num1, num2):
print(num1 / num2)
foo = my_decorator(divide)
foo(5, 2)
Sintaktik şəkər
Dekoratoru
foo = my_decorator(divide)
kimi istifadə etmək əvəzinə, daha qısa və oxunaqlı bir sintaksis var.
Funksiyanın adının önünə @ işarəsi qoyaraq istifadə edə bilərik:
def my_decorator(func):
def wrapper(*args, **kwargs):
print("Funksiyadan qabaq işləyəcək.")
func(*args, **kwargs)
print("Funksiyadan sonra işləyəcək.")
return wrapper
@my_decorator
def divide(num1, num2):
print(num1 / num2)
divide(5, 2)
Hətta bir neçə dekoratoru bir funksiyanın üzərinə zəncirvari (chaining) şəkildə də yazmaq mümkündür:
@my_decorator1
@my_decorator2
@my_decorator3
def divide(num1, num2):
print(num1 / num2)
@wraps dekoratoru
Python-da __name__, __doc__ və s. kimi daxili sehrli (magic) dəyişənlər var.
Məsələn:
print(print.__name__) # "print" qaytarır
Bu məntiqlə biz aşağıdakı kodu icra etsək, bizə "divide" qaytarmalı idi, amma
"wrapper" qaytardı:
print(divide.__name__)
Səbəb odur ki, biz divide-ı my_decorator-a mənimsətmişik. O da bizə
daxilindəki wrapper() metodunu qaytarır.
Bu problemi həll etmək və əsas funksiyanın kimliyini (meta məlumatlarını)
qorumaq üçün Python-un functools kitabxanasındakı @wraps dekoratorundan
istifadə edəcəyik:
from functools import wraps
def my_decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
print("Funksiyadan qabaq işləyəcək.")
func(*args, **kwargs)
print("Funksiyadan sonra işləyəcək.")
return wrapper
@my_decorator
def divide(num1, num2):
print(num1 / num2)
print(divide.__name__)
Daxili funksiya olan wrapper() funksiyasının üstünə @wraps dekoratorunu
yazıb, parametr kimi də func dəyərini verdikdən sonra divide.__name__ artıq
doğru şəkildə "divide" qaytaracaq.
Yekun həll
İndi isə ən başdakı sıfıra bölmə (ZeroDivisionError) məsələsinin yekun həllinə
baxaq:
from functools import wraps
def my_decorator(func):
@wraps(func)
def wrapper(num1, num2):
if num2 == 0:
print("Sıfıra bölmək olmaz!")
else:
func(num1, num2)
return wrapper
@my_decorator
def divide(num1, num2):
print(num1 / num2)
divide(5, 0)
divide(5, 2)
Nəticə:
Sıfıra bölmək olmaz!
2.5
// terminlər
- dekorator decorator
- Funksiya və ya metodun davranışını onu dəyişmədən genişləndirən quruluş.
- ↗ Ətraflı
- freymvörk framework
- Tətbiq inkişafını sürətləndirmək üçün əvvəlcədən qurulmuş struktur və alətlər toplusu.
- ↗ Ətraflı
- sintaksis syntax
- Proqramlaşdırma dilinin qrammatik qaydaları — kodun necə yazılmalı olduğunu müəyyən edən struktur.
- ↗ Ətraflı
- sintaktik şəkər syntactic sugar
- Eyni məntiqi daha oxunaqlı və qısa yazmağa imkan verən əlavə sintaksis forması.
- ↗ Ətraflı
// şərhlər