I decoratori sono funzioni che prendono un'altra funzione e restituiscono una nuova funzione con comportamenti estesi o modificati. Vengono spesso utilizzati per registrazione, controllo dei diritti, caching, tempo di esecuzione e altro.
I decoratori vengono implementati utilizzando chiusure (closure) o classi che implementano il metodo __call__. La sintassi classica:
# Decoratore semplice def simple_decorator(func): def wrapper(*args, **kwargs): print("Prima della chiamata della funzione") result = func(*args, **kwargs) print("Dopo la chiamata della funzione") return result return wrapper @simple_decorator def my_func(): print("Funzione principale") my_func()
Il risultato sarà:
Prima della chiamata della funzione
Funzione principale
Dopo la chiamata della funzione
Il principale utilizzo — incapsulamento della logica ripetitiva al di fuori della logica di business principale.
Dettagli:
functools.wraps, si perde il nome della funzione originale e il suo docstring.Domanda: "Se decoro una funzione con un decoratore e poi cerco di ottenere il nome della funzione tramite __name__, cosa vedrò e come preservare il nome originale?"
Risposta:
Per impostazione predefinita, il nome cambierà in quello del wrapper (di solito wrapper). Per preservare i metadati originali, utilizza functools.wraps:
import functools def dec(f): @functools.wraps(f) def wrapper(*args): return f(*args) return wrapper @dec def foo(): pass print(foo.__name__) # stamperà 'foo', non 'wrapper'
Storia
In una grande automazione delle risorse, dopo aver avvolto le funzioni con decoratori, il sistema di autotest è stato rotto, poiché utilizzava la riflessione sui nomi delle funzioni di test. Il problema era l'assenza di functools.wraps.
Storia
Un decoratore che aggiunge registrazione non supportava funzioni con firme diverse, poiché non venivano utilizzati *args, **kwargs. Alcune funzioni semplicemente si rompevano Silent Fail.
Storia
In un progetto con autorizzazione in REST API, un sviluppatore ha implementato un decoratore con parametri, ma ha dimenticato di annidare correttamente le funzioni (c'era un annidamento a due livelli invece di tre). Di conseguenza, il decoratore non poteva accettare parametri.