Ana içeriğe geç
Modül 9 · Ders 4 ders 4Kurstaki 36/36~14 min
Modül dersleri (4/4)

Dekoratörler: işlevleri değiştiren işlevler

dekoratör, başka bir işlevi alıp bir değer döndüren bir işlevdir. bunun "dekore edilmiş" versiyonu. Davranış eklemenin güçlü bir yoludur (günlüğe kaydetme, zamanlama, önbelleğe alma, yeniden deneme, doğrulama) orijinal fonksiyon.

Değer olarak işlevler

Python'da işlevler nesnelerdir: onları iletebilir, geri verebilir, atayabilirsiniz.

Python
def saluta():
    print("ciao")

f = saluta   # NIENTE parentesi: assegno la funzione
f()          # 'ciao'

Minimal bir dekoratör

Python
def log(func):
    def wrapper(*args, **kwargs):
        print(f"chiamo {func.__name__}")
        risultato = func(*args, **kwargs)
        print(f"ho finito {func.__name__}")
        return risultato
    return wrapper

def somma(a, b):
    return a + b

somma = log(somma)   # ora somma è "wrapper"
somma(2, 3)
# chiamo somma
# ho finito somma
# 5

@decorator sözdizimi

somma = log(somma) ödevi @ ile daha zarif bir şekilde yazılmıştır:

Python
@log
def somma(a, b):
    return a + b

Tamamen aynı şeydir. @log yukarıdaki tanım şuna eşdeğerdir: "bu işlev tanımlanır tanımlanmaz onu log(function) ile değiştirin".

*args, **kwargs: herhangi bir imzayı kabul et

Python
def wrapper(*args, **kwargs):
    return func(*args, **kwargs)

*args konumsal argümanları, **kwargs anahtar kelime argümanlarını toplar. İzin verdiler herhangi bir işlevle çalışan sarmalayıcılar yazarsınız.

functools.wraps: meta verileri korur

wraps olmadan, dekore edilen işlev __name__, __doc__ kaybeder, ipuçları:

Python
from functools import wraps

def log(func):
    @wraps(func)
    def wrapper(*args, **kwargs):
        ...
        return func(*args, **kwargs)
    return wrapper

Örnek: @timeit

Python
from functools import wraps
import time

def timeit(func):
    @wraps(func)
    def wrapper(*args, **kwargs):
        t0 = time.perf_counter()
        risultato = func(*args, **kwargs)
        dt = time.perf_counter() - t0
        print(f"{func.__name__} ha impiegato {dt*1000:.2f} ms")
        return risultato
    return wrapper

@timeit
def somma_grande():
    return sum(range(10**6))

somma_grande()

Stdlib'den dekoratörler

Python
from functools import lru_cache

@lru_cache(maxsize=None)
def fib(n):
    if n < 2: return n
    return fib(n - 1) + fib(n - 2)

fib(100)  # istantaneo grazie alla cache
  • @lru_cache — saf bir fonksiyonun sonuçlarını hafızaya alır.
  • @property — bir yöntemi öznitelik olarak gösterir (OOP modülünde görülür).
  • @staticmethod, @classmethod — self'nin iletilme biçimini değiştirin.

Argümanları olan dekoratörler

Dekoratörler argümanları da kabul edebilir. Bunu uygulamak için üç düzeyde iç içe geçmiş fonksiyon yazmalısınız: en dıştaki fonksiyon dekoratörün argümanlarını alır, ortadaki fonksiyon dekore edilmiş fonksiyonu alır ve en içteki fonksiyon (wrapper) çağrılar sırasında dekore edilmiş fonksiyona iletilen gerçek argümanları alır.

Kendin dene

Egzersiz#python.m9.l4.e1
Denemeler: 0Yükleniyor…

Dekore edilmiş işlevi çağıran ve sonucunu * 2 döndüren bir dekoratör 'double_result' yazın. Bunu x + 1 döndüren bir 'f(x)' fonksiyonuna uygulayın. 'f(10)'u değerlendirin.

Düzenleyici yükleniyor…
İpucu göster

Sarmalayıcı func'ı çağırır ve sonucu 2 ile çarparak döndürür.

3 denemeden sonra çözüm mevcut

Alıştırmayı gözden geçirin

Egzersiz#python.m9.l4.e2
Denemeler: 0Yükleniyor…

Özyinelemeli fib(n) işlevini hızlandırmak için functools.lru_cache kullanın. Fib(30)'u hesaplayın ve 'r'ye atayın. 'r'yi değerlendirin.

Düzenleyici yükleniyor…
İpucu göster

@lru_cache(maxsize=None) def'in üstünde.

3 denemeden sonra çözüm mevcut

Ek zorluk

Egzersiz#python.m9.l4.e3
Denemeler: 0Yükleniyor…

Define a decorator `double_result` that doubles (multiplies by 2) the return value of the decorated function. Apply the decorator to the function `def sum_nums(a, b): return a + b`. Finally, evaluate `sum_nums(3, 4)`.

Düzenleyici yükleniyor…
İpucu göster

Sarmalayıcı func(*args, **kwargs) öğesini çağırır ve sonucu 2 ile çarpar.

3 denemeden sonra çözüm mevcut