Les décorateurs sont des fonctions qui prennent une autre fonction et renvoient une nouvelle fonction avec un comportement étendu ou modifié. Ils sont souvent utilisés pour le journalisation, la vérification des droits, le caching, la mesure du temps d'exécution, etc.
Les décorateurs sont implémentés à l'aide de fermetures (closure) ou de classes implémentant la méthode __call__. La syntaxe classique :
# Décorateur simple def simple_decorator(func): def wrapper(*args, **kwargs): print("Avant d'appeler la fonction") result = func(*args, **kwargs) print("Après avoir appelé la fonction") return result return wrapper @simple_decorator def my_func(): print("Fonction principale") my_func()
Le résultat sera :
Avant d'appeler la fonction
Fonction principale
Après avoir appelé la fonction
Principale utilité — encapsulation de la logique répétitive en dehors de la logique métier principale.
Subtilités :
functools.wraps, le nom de la fonction d'origine et son docstring sont perdus.Question : "Si je décore une fonction avec un décorateur, puis que j'essaie d'obtenir le nom de la fonction via __name__, que vais-je voir et comment conserver le nom d'origine ?"
Réponse :
Par défaut, le nom changera pour le nom de l'enveloppe (généralement wrapper). Pour conserver les métadonnées d'origine, utilisez 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__) # affichera 'foo', pas 'wrapper'
Histoire
Dans une grande automatisation des ressources, après avoir enveloppé des fonctions avec des décorateurs, le système de tests automatisés s'appuyant sur la réflexion des noms des fonctions de test a cessé de fonctionner. Le problème était l'absence de functools.wraps.
Histoire
Un décorateur ajoutant des journaux ne supportait pas les fonctions avec des signatures différentes, car *args, **kwargs n'étaient pas utilisés. Certaines fonctions étaient simplement cassées (échec silencieux).
Histoire
Dans un projet avec une authentification via REST API, le développeur a implémenté un décorateur avec des paramètres mais a oublié de correctement imbriquer les fonctions (il y avait une imbrication à deux niveaux au lieu de trois). En conséquence, le décorateur ne pouvait pas accepter de paramètres.