Lecții din modul (4/4)
Decoratori: funcții care modifică funcții
Un decorator este o funcție care preia o altă funcție și returnează a versiunea „decorată” a acestuia. Este o modalitate puternică de a adăuga comportament (înregistrare, sincronizare, stocare în cache, reîncercare, validare) fără a atinge corpul funcția originală.
Funcționează ca valori
În Python, funcțiile sunt obiecte: le puteți transmite, le puteți returna, le puteți atribui.
def saluta():
print("ciao")
f = saluta # NIENTE parentesi: assegno la funzione
f() # 'ciao'Un decorator minimal
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 sintaxă
Misiunea somma = log(somma) este scrisă mai elegant cu @:
@log
def somma(a, b):
return a + bEste exact același lucru. @log de mai sus definiția este echivalentă cu:
„de îndată ce această funcție este definită, înlocuiți-o cu log(function)”.
*args, **kwargs: acceptați orice semnătură
def wrapper(*args, **kwargs):
return func(*args, **kwargs)*args colectează argumente de poziție, **kwargs argumente de cuvinte cheie. Au lăsat
scrii wrapper-uri care funcționează cu orice funcție.
functools.wraps: păstrează metadatele
Fără wraps, funcția decorată pierde __name__, __doc__, indicii:
from functools import wraps
def log(func):
@wraps(func)
def wrapper(*args, **kwargs):
...
return func(*args, **kwargs)
return wrapperExemplu: @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()Decoratori de la stdlib
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— memorează rezultatele unei funcții pure.@property— expune o metodă ca atribut (văzut în modulul OOP).@staticmethod,@classmethod— modificați modul în careselfeste transmis.
Decoratori cu argumente
Decoratorii pot accepta și argumente. Pentru a implementa acest lucru, trebuie să scrieți trei niveluri de funcții imbricate: funcția cea mai exterioară primește argumentele decoratorului, funcția din mijloc primește funcția decorată, iar funcția cea mai interioară (wrapper) primește argumentele reale transmise funcției decorate în timpul apelurilor.
Încercați-l singur
Scrieți un decorator `double_result` care apelează funcția decorată și returnează rezultatul acesteia * 2. Aplicați-l unei funcții `f(x)` care returnează x + 1. Evaluați `f(10)`.
Afișează indiciu
Wrapper-ul apelează func și returnează rezultatul înmulțit cu 2.
Soluție disponibilă după 3 încercări
Exercițiu de revizuire
Utilizați functools.lru_cache pentru a accelera o funcție recursivă fib(n). Calculați fib(30) și atribuiți lui `r`. Evaluează `r`.
Afișează indiciu
@lru_cache(maxsize=None) deasupra def.
Soluție disponibilă după 3 încercări
Provocare suplimentară
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)`.
Afișează indiciu
Wrapper-ul apelează func(*args, **kwargs) și înmulțește rezultatul cu 2.
Soluție disponibilă după 3 încercări