Məzmuna keçin
  • Kateqoriyalar
  • Ən yeni
  • Teqlər
  • Populyar
Yığmaq
Brend loqosu
  1. Əsas səhifə
  2. Python
  3. Python və "örtüklər"

Python və "örtüklər"

Planlaşdırılıb Sabitlənib Kilidlənib Köçürülüb Python
1 Yazı 1 Yazarlar 80 Baxış
  • Ən köhnədən yeniyə
  • Ən yenidən köhnəyə
  • Ən çox səs
Cavab ver
  • Mövzu olaraq cavablandır
🔑 Daxil ol
Bu mövzu silindi. Yalnız mövzu idarəçiliyi imtiyazlarına malik olan istifadəçilər onu görə bilər.
  • N Oflayn
    N Oflayn
    Nicat Məmmədov
    yazdı sonuncu dəfə tərəfindən redaktə edilib
    #1

    Decorator-lar (örtük və ya bəzəyici kimi tərcümə oluna bilər) bir çox Python kitabxanası və çərçivələrində (framework) istifadə olunan özəllikdir.
    61KrcZrlDlL-85634531-3.jpg

    Hər hansı metodu istifadə etməzdən qabaq görüləcək işləri özündə ehtiva edir. Məsələn, Django çərçivəsində hansısa spesifik bir keçiddə istifadəçinin öz hesabına daxil olub-olmadığını yoxlayırıq.

    @login_required
    def account_view(request):
        return HttpResponse('Gizli səhifəyə xoş gəldiniz!')
    

    account_view funksiyasının üzərinə yazılan örtük (decorator) ora sorğu göndərməzdən qabaq, hesaba daxil olub olmadığını yoxayır. Əgər daxil olubsa cavab qaytarır, yoxsa giriş (login) səhifəsinə yönləndirir.
    Bəs bunun işləmə prinsipi nədir? Necə olur ki, həmən funksiya çağırılmamışdan qabaq login_required işə düşür?
    Əslində işləmə prinsipi çox sadədir. İşin sehri sadəcə qısayol sintaksisindədir (syntactic sugar).

    İ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ı.
    Örtük (decorator) vasitəsi ilə 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 yartdıq. Hansı ki, içində wrapper adında daxili (inner) funksiya var. Əlavə olaraq, bir də 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 ilk öncə my_decorator işə düşür, ardınca içindəki wrapper(). Ən sonda isə divide().
    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

    Örtüklər və arqumentlər

    Əgər bizim örtük 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)
    

    Traceback (most recent call last):
      File “main.py”, line 12, in
        foo(5, 2)
    TypeError: wrapper() takes 0 positional arguments but 2 were given

    Bu xətanı biz ona görə aldıq ki, 4-cü sətirdə ç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.
    Örtükdəki daxili funksiyamız olan wrapper() əsas funksiyamızın (divider()) 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 bizdəki kimi 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ı örtüklər yazmalı olacağıq. Bunun həll yolu *args və **kwargs ikilisidir.
    *args funksiyaya ötürülən istənilən mövqeli (positional) arqumentləri, **kwargs isə açar sözlü (keyword) arqumentləri qəbul edir.

    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)
    

    Qısayol sintaksisi (syntactic sugar)

    Örtüyü foo = my_decorator(divide) kimi yazmaq əvəzinə

    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)
    

    kimi yaza bilərik.
    Bir neçə örtük funksiyasını eyni bir funksiyanın üzərində istifadə etmək mümkündür:

    @my_decorator1
    @my_decorator2
    @my_decorator3
    def divide(num1, num2):
      print(num1/num2)
    

    Nəzərə almaq lazımdır ki, Python örtükləri aşağıdan yuxarıya doğru oxuyub, icra edəcək. Yəni birinci @my_decorator3 icra olunacaq, ardınca @my_decorator2 və sonda @my_decorator3.

    @wraps decorator

    Python-da __name__, __doc__ və s. kimi hazır gələn (built-in) sehrli (magic) dəyişənlər var. Məsələn, __name__ bizə funksiyanın adını verir:

    print(print.__name__)
    

    “print” funksiyasının adını qaytarır.
    Bu məntiqlə biz print(divide.__name__) kodunu icra etsək divide qaytarmalı idi, amma “wrapper” qaytardı. Çünki, biz divide-ı my_docator-a mənimsətmişik. O da bizə wrapper() metodunu qaytarır. Problemi həll etmək üçün Python-un @wraps decorator-undan 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 örtüyünü qoyub, parametr kimi də func dəyərini verdikdən sonra divide.__name__ bizə konsolda “divide” qaytaracaq.

    Və, sonda məsələnin həllinə gələk:

    from functools import wraps
     
    def my_decorator(func):
      @wraps(func)
      def wrapper(num1, num2):
          if num2 == 0:
              print("You can not divide by zero.")
          else:
              func(num1, num2)
      return wrapper
     
    @my_decorator
    def divide(num1, num2):
      print(num1/num2)
     
    divide(5, 0)
    divide(5, 2)
    

    Nəticə:

    "You can not divide by zero."
    
    2.5
    
    1 cavab Son cavab
    Cavab ver
    • Mövzu olaraq cavablandır
    🔑 Daxil ol
    • Ən köhnədən yeniyə
    • Ən yenidən köhnəyə
    • Ən çox səs




    Bilik paylaşdıqca artan bir sərvətdir
    • Daxil ol

    • Sizin hesabınız yoxdur? Qeydiyyatdan keç

    • Axtarış etmək üçün daxil olun və ya qeydiyyatdan keçin.
    • İlk yazı
      Son yazı
    0
    • Kateqoriyalar
    • Ən yeni
    • Teqlər
    • Populyar