I decoratori in Python sono storicamente emersi come un modo per rendere il codice più compatto e leggibile, incapsulando comportamenti ripetitivi attorno a funzioni e metodi. Prima dell'introduzione della sintassi @decorator, venivano applicati esplicitamente, il che complicava la comprensione del codice. Oggi, i decoratori giocano un ruolo chiave nell'organizzazione della logica, nei controlli ripetitivi, nel logging, nella memorizzazione e altro ancora.
Il problema nell'uso dei decoratori è gestire correttamente le differenze tra funzioni, metodi dell'istanza e metodi statici/classe. Spesso si verificano errori legati alla perdita di informazioni sul metodo, al binding tardivo/precoce di self e alla firma della funzione (signature).
La soluzione consiste nell'utilizzare il modulo functools.wraps per mantenere i metadati quando si scrivono decoratori universali e prestare attenzione al tipo di oggetto decorato (ad esempio, considerando correttamente che i metodi dell'istanza ricevono self come primo argomento).
Esempio di codice:
import functools def my_decorator(func): @functools.wraps(func) def wrapper(*args, **kwargs): print(f"Prima della funzione: {func.__name__}") result = func(*args, **kwargs) print(f"Dopo la funzione: {func.__name__}") return result return wrapper class Esempio: @my_decorator def metodo(self, x): print(f"Metodo chiamato con {x}") ex = Esempio() ex.metodo(5)
Caratteristiche chiave:
self come primo argomento.functools.wraps.Se si utilizza un decoratore scritto per una funzione su un metodo di classe, è obbligatorio utilizzare functools.wraps e cosa succederà a self?
No, il decoratore funziona, ma senza wraps si perde il nome della funzione, il supporto dell'IDE e la documentazione. Self rimarrà comunque il primo parametro, ma la perdita dei metadati complicherà il debug e la riflessione.
def bad_decorator(f): def wrapper(*args, **kwargs): print("decorato") return f(*args, **kwargs) return wrapper class Test: @bad_decorator def foo(self): pass print(Test().foo.__name__) # wrapper
È possibile applicare lo stesso decoratore sia a un metodo dell'istanza che a un metodo statico?
Sì, ma bisogna ricordare che i metodi statici non ricevono self come primo argomento. Se il decoratore è progettato per lavorare con self, si verificheranno errori con @staticmethod/@classmethod.
Come influisce il decoratore sulla firma del metodo e sul completamento automatico?
Semplicemente: senza functools.wraps, la firma e il docstring andranno persi, e l'IDE e molti strumenti di suggerimento smetteranno di funzionare correttamente.
functools.wraps — perdita del nome della funzione, docstring, rende il debug più complesso.Caso negativo: Decoratori senza functools.wraps in tutti i metodi di una classe.
Vantaggi: prototipo veloce, funziona.
Svantaggi: impossibile cercare errori nello stack, l'IDE non suggerisce la firma.
Caso positivo: I decoratori usano functools.wraps, il codice è documentato.
Vantaggi: leggibilità, manutenzione, comfort dell'IDE.
Svantaggi: aggiunta minima alla sintassi e attenzione.