Que sont les décorateurs en Python
Si vous avez codé en Python plus d'une semaine, vous avez forcément vu le symbole @ devant une fonction. C'est un décorateur — un outil de programmation puissant et élégant.
Les décorateurs en Python permettent d'« envelopper » une fonction dans une autre. Ils modifient ou étendent le comportement d'une fonction sans toucher à son code source. Les décorateurs sont omniprésents : dans Flask, Django, FastAPI, pytest, et même dans la bibliothèque standard de Python.
Fonctionnement des décorateurs
Les bases des décorateurs
Un décorateur est une fonction qui prend une autre fonction en argument et retourne une nouvelle fonction (ou la fonction modifiée).
def decorateur(func):
def wrapper():
print("Avant l'appel")
func()
print("Après l'appel")
return wrapper
@decorateur
def dire_bonjour():
print("Bonjour !")
dire_bonjour()
Résultat de l'exécution :
Avant l'appel
Bonjour !
Après l'appel
Le symbole @decorateur est un sucre syntaxique équivalent à :
dire_bonjour = decorateur(dire_bonjour)
Mécanisme interne des décorateurs
Pour bien comprendre les décorateurs en Python, il faut connaître ces concepts clés :
- En Python, les fonctions sont des objets de première classe
- Les fonctions peuvent être passées comme arguments à d'autres fonctions
- On peut définir des fonctions à l'intérieur d'autres fonctions
- Le mécanisme de fermeture (closure) est essentiel
Modèle universel de décorateur
Voici le modèle minimal d'un décorateur :
def decorateur(func):
def wrapper(*args, **kwargs):
# Actions avant l'exécution de la fonction
resultat = func(*args, **kwargs)
# Actions après l'exécution de la fonction
return resultat
return wrapper
Exemples pratiques de décorateurs
Décorateur de mesure du temps d'exécution
import time
def chronometre(func):
def wrapper(*args, **kwargs):
debut = time.time()
resultat = func(*args, **kwargs)
duree = time.time() - debut
print(f"{func.__name__} a pris {duree:.4f} sec")
return resultat
return wrapper
@chronometre
def fonction_lente():
time.sleep(1)
return "Opération terminée"
fonction_lente()
Ce décorateur est utile pour profiler les performances des fonctions et optimiser votre code.
Décorateur de vérification d'autorisation
def require_admin(func):
def wrapper(utilisateur, *args, **kwargs):
if utilisateur != "admin":
raise PermissionError("Accès refusé")
return func(utilisateur, *args, **kwargs)
return wrapper
@require_admin
def supprimer_base(utilisateur):
print("Base de données supprimée !")
return True
supprimer_base("admin") # Exécution réussie
Ce décorateur renforce la sécurité de votre application en contrôlant l'accès aux fonctions critiques.
Décorateur de journalisation
import logging
from functools import wraps
def journaliser_appels(func):
@wraps(func)
def wrapper(*args, **kwargs):
logging.info(f"Appel de la fonction {func.__name__} avec arguments {args}, {kwargs}")
try:
resultat = func(*args, **kwargs)
logging.info(f"Fonction {func.__name__} exécutée avec succès")
return resultat
except Exception as e:
logging.error(f"Erreur dans la fonction {func.__name__} : {e}")
raise
return wrapper
Décorateurs avec arguments
Création d'une fabrique de décorateurs
Lorsqu'un décorateur doit accepter des paramètres, on crée une fabrique de décorateurs :
def repeter(n):
def decorateur(func):
def wrapper(*args, **kwargs):
resultats = []