Décorateurs#
avancé
Principe#
Un décorateur est une fonction qui prend une fonction en argument et renvoie une fonction (souvent une version enrichie).
def journaliser(fonction):
def wrapper(*args, **kwargs):
print(f"appel de {fonction.__name__}")
resultat = fonction(*args, **kwargs)
print(f"retour : {resultat}")
return resultat
return wrapper
@journaliser
def addition(a, b):
return a + b
addition(2, 3)
# appel de addition
# retour : 5
Équivalent à : addition = journaliser(addition).
Préserver les métadonnées : functools.wraps#
from functools import wraps
def journaliser(fonction):
@wraps(fonction) # préserve __name__, __doc__ de la fonction décorée
def wrapper(*args, **kwargs):
return fonction(*args, **kwargs)
return wrapper
Piège
Sans @wraps, addition.__name__ devient "wrapper" après décoration : perturbe l'introspection, les docs et les tests.
Décorateur avec paramètres#
def repeter(n):
def decorateur(fonction):
@wraps(fonction)
def wrapper(*args, **kwargs):
resultat = None
for _ in range(n):
resultat = fonction(*args, **kwargs)
return resultat
return wrapper
return decorateur
@repeter(3)
def dire_bonjour():
print("bonjour")
Décorateurs intégrés courants#
class Cercle:
def __init__(self, rayon):
self._rayon = rayon
@property
def rayon(self):
return self._rayon
@staticmethod
def depuis_diametre(d):
return Cercle(d / 2)
@classmethod
def unite(cls):
return cls(1)
Décorateurs de functools#
from functools import lru_cache, cached_property
@lru_cache(maxsize=None) # mémoïse les appels : arguments -> résultat
def fibonacci(n):
return n if n < 2 else fibonacci(n - 1) + fibonacci(n - 2)
class Maillage:
@cached_property # calculé au 1er accès, puis conservé sur l'instance
def volume(self):
return sum(c.volume for c in self.cellules)
Piège
lru_cache exige des arguments hachables (pas de list ni de dict) et suppose la fonction pure : mémoïser une fonction qui lit un fichier ou dépend de l'heure renvoie des résultats périmés.