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.
def saluta():
print("ciao")
f = saluta # NIENTE parentesi: assegno la funzione
f() # 'ciao'Minimal bir dekoratör
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:
@log
def somma(a, b):
return a + bTamamen 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
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ı:
from functools import wraps
def log(func):
@wraps(func)
def wrapper(*args, **kwargs):
...
return func(*args, **kwargs)
return wrapperÖrnek: @timeit
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
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
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.
İ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
Ö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.
İpucu göster
@lru_cache(maxsize=None) def'in üstünde.
3 denemeden sonra çözüm mevcut
Ek zorluk
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)`.
İpucu göster
Sarmalayıcı func(*args, **kwargs) öğesini çağırır ve sonucu 2 ile çarpar.
3 denemeden sonra çözüm mevcut