OUJOOD.COM
Un décorateur est une fonction qui prend une autre fonction en argument, lui ajoute un comportement, et retourne une version modifiée. Concrètement, au lieu de répéter le même code dans dix fonctions différentes — un contrôle d'accès, une mesure de temps, un log — vous l'écrivez une seule fois sous forme de décorateur et vous l'appliquez avec @nom_du_decorateur.
Derrière la syntaxe @, il n'y a rien de magique : c'est du Python ordinaire. Ce cours part de zéro pour vous montrer comment les décorateurs fonctionnent, comment en créer, et quand s'en servir.
Prérequis : les fonctions sont des objets en Python
Pour comprendre les décorateurs, il faut d'abord savoir qu'en Python, une fonction est un objet comme un autre. On peut la stocker dans une variable, la passer en argument, ou la retourner depuis une autre fonction.
def saluer():
print("Bonjour !")
# On peut stocker une fonction dans une variable
action = saluer
action()
# Résultat : Bonjour !
# On peut passer une fonction en argument
def executer(fonction):
fonction()
executer(saluer)
# Résultat : Bonjour !
Construire un décorateur à la main
Un décorateur est une fonction qui reçoit une fonction, définit une fonction interne (wrapper) qui ajoute du comportement, et retourne ce wrapper. Voici la structure de base :
def mon_decorateur(fonction):
def wrapper():
print("Avant l'appel")
fonction()
print("Après l'appel")
return wrapper
def dire_bonjour():
print("Bonjour !")
# Appliquer le décorateur manuellement
dire_bonjour = mon_decorateur(dire_bonjour)
dire_bonjour()
# Résultat :
# Avant l'appel
# Bonjour !
# Après l'appel
La syntaxe @ : le raccourci officiel
La ligne dire_bonjour = mon_decorateur(dire_bonjour) est exactement ce que fait Python quand vous écrivez @mon_decorateur juste au-dessus d'une fonction. C'est un raccourci syntaxique, rien de plus.
def mon_decorateur(fonction):
def wrapper():
print("Avant l'appel")
fonction()
print("Après l'appel")
return wrapper
# Équivalent à : dire_bonjour = mon_decorateur(dire_bonjour)
@mon_decorateur
def dire_bonjour():
print("Bonjour !")
dire_bonjour()
# Résultat :
# Avant l'appel
# Bonjour !
# Après l'appel
Décorer des fonctions avec des arguments
Le wrapper doit accepter et transmettre les arguments de la fonction décorée. On utilise *args et **kwargs pour que le décorateur fonctionne avec n'importe quelle signature.
def logger(fonction):
def wrapper(*args, **kwargs):
print(f"Appel de '{fonction.__name__}' avec {args} {kwargs}")
resultat = fonction(*args, **kwargs)
print(f"Retour : {resultat}")
return resultat
return wrapper
@logger
def additionner(a, b):
return a + b
additionner(3, 5)
# Résultat :
# Appel de 'additionner' avec (3, 5) {}
# Retour : 8
Préserver la signature avec @wraps
Quand vous décorez une fonction, Python remplace son nom et sa documentation par ceux du wrapper. Pour conserver les métadonnées de la fonction originale, utilisez @wraps du module functools. C'est une bonne pratique à adopter systématiquement.
from functools import wraps
def logger(fonction):
@wraps(fonction) # Préserve __name__, __doc__, etc.
def wrapper(*args, **kwargs):
print(f"Appel de '{fonction.__name__}'")
return fonction(*args, **kwargs)
return wrapper
@logger
def multiplier(a, b):
"""Multiplie deux nombres."""
return a * b
# Sans @wraps, __name__ retournerait 'wrapper'
print(multiplier.__name__)
# Résultat : multiplier
print(multiplier.__doc__)
# Résultat : Multiplie deux nombres.
Cas d'usage concrets
Mesurer le temps d'exécution
import time
from functools import wraps
def chronometre(fonction):
@wraps(fonction)
def wrapper(*args, **kwargs):
debut = time.time()
resultat = fonction(*args, **kwargs)
duree = time.time() - debut
print(f"{fonction.__name__} exécutée en {duree:.4f}s")
return resultat
return wrapper
@chronometre
def calcul_lent():
time.sleep(0.5)
return "terminé"
calcul_lent()
# Résultat : calcul_lent exécutée en 0.5002s
Vérifier qu'un utilisateur est connecté
from functools import wraps
def connexion_requise(fonction):
@wraps(fonction)
def wrapper(utilisateur, *args, **kwargs):
if not utilisateur.get("connecte"):
print("Accès refusé : vous devez être connecté.")
return None
return fonction(utilisateur, *args, **kwargs)
return wrapper
@connexion_requise
def voir_profil(utilisateur):
print(f"Profil de {utilisateur['nom']}")
user_connecte = {"nom": "Alice", "connecte": True}
user_deconnecte = {"nom": "Bob", "connecte": False}
voir_profil(user_connecte)
# Résultat : Profil de Alice
voir_profil(user_deconnecte)
# Résultat : Accès refusé : vous devez être connecté.
Décorateur avec ses propres arguments
Pour créer un décorateur qui accepte lui-même des paramètres, il faut ajouter un niveau d'imbrication : une fonction externe reçoit les arguments du décorateur, et retourne le vrai décorateur.
from functools import wraps
def repeter(n):
# n est l'argument du décorateur
def decorateur(fonction):
@wraps(fonction)
def wrapper(*args, **kwargs):
for _ in range(n):
fonction(*args, **kwargs)
return wrapper
return decorateur
@repeter(3)
def dire_bonjour(nom):
print(f"Bonjour, {nom} !")
dire_bonjour("Alice")
# Résultat :
# Bonjour, Alice !
# Bonjour, Alice !
# Bonjour, Alice !
Empiler plusieurs décorateurs
On peut appliquer plusieurs décorateurs sur une même fonction. Python les applique de bas en haut : le décorateur le plus proche de la fonction est appliqué en premier.
from functools import wraps
def majuscules(fonction):
@wraps(fonction)
def wrapper(*args, **kwargs):
resultat = fonction(*args, **kwargs)
return resultat.upper()
return wrapper
def exclamation(fonction):
@wraps(fonction)
def wrapper(*args, **kwargs):
resultat = fonction(*args, **kwargs)
return resultat + " !!!"
return wrapper
# Ordre d'application : exclamation(majuscules(saluer))
@exclamation
@majuscules
def saluer(nom):
return f"bonjour {nom}"
print(saluer("Alice"))
# Résultat : BONJOUR ALICE !!!
Par carabde | Mis à jour le 21 avril 2026