Question 1Laquelle des propositions suivantes a le même sens que mettre @logger au-dessus de def greet(): ... ?
Décorateurs — ajouter du comportement aux fonctions avec @
Apprends à utiliser les décorateurs Python avec @ pour ajouter du comportement à tes fonctions sans modifier leur code.
À la fin de l'article précédent sur les lambdas, tu avais vu les principales façons de traiter les fonctions comme des valeurs. Pour conclure, fixons les décorateurs — la syntaxe dédiée pour superposer un comportement supplémentaire à une fonction.
Qu'est-ce qu'un décorateur ?
Un décorateur est une manière d'ajouter un comportement avant et après une fonction sans modifier la fonction elle-même. Des choses comme « logger l'appel », « chronométrer l'exécution » ou « mettre le résultat en cache » — du comportement partagé que tu veux superposer à plein de fonctions — peuvent vivre à un seul endroit.
La syntaxe tient en une ligne au-dessus de la définition de fonction : @nom_decorateur. Python lit ça comme « la même chose que func = nom_decorateur(func) ».
@logger au-dessus de def greet(): fait exécuter à Python greet = logger(greet) en interne, remplaçant greet par une nouvelle fonction enveloppée par logger.# Le décorateur lui-même (une fonction d'ordre supérieur qui prend et retourne une fonction)
def logger(func):
def wrapper():
print("=== début ===")
func() # appelle la fonction d'origine
print("=== fin ===")
return wrapper
# Côté appelant : ajoute juste @
@logger
def greet(): # → logger(greet)
print("Bonjour")
greet()
# === début ===
# Bonjour
# === fin ===
# En interne, équivalent à :
# def greet():
# print("Bonjour")
# greet = logger(greet)
Le décorateur de base — envelopper une fonction avec wrapper
Le squelette d'un décorateur est une forme en 3 étapes : la fonction externe prend func, la fonction interne (par convention wrapper) appelle func(), et tu fais return wrapper. Le wrapper qui garde func accessible pendant qu'il s'exécute est exactement une clôture.
Tout ce que tu écris avant et après func() s'exécute à chaque appel de la fonction décorée.
def logger(func):
def wrapper(*args, **kwargs):
print(f"[LOG] exécution de {func.__name__}")
result = func(*args, **kwargs)
print(f"[LOG] {func.__name__} terminé")
return result
return wrapper
@logger
def greet(name):
return f"Bonjour, {name}"
print(greet("Alice"))
# [LOG] exécution de greet
# [LOG] greet terminé
# Bonjour, Alice
- greet est remplacée par la fonction wrapper
- Le corps original de
greetsurvit commefuncdanswrapper
funccontient lagreetoriginale- Construit
wrapperà l'intérieur et le retourne
- Exécute pré →
func()→ post dans l'ordre - De l'extérieur, c'est la nouvelle
greet
Passer n'importe quels arguments avec *args / **kwargs
Jusqu'à présent, wrapper ne prenait pas d'arguments. Quand tu veux décorer des fonctions qui prennent des arguments, utilise *args / **kwargs pour accepter n'importe quels arguments tels quels et les transmettre directement à `func`.
Ça transforme le décorateur en un décorateur générique qui marche avec n'importe quelle signature de fonction. Il gère add(2, 3) (positionnel) et add(2, 3, name="ABC") (nommé) avec le même décorateur.
def log_call(func):
def wrapper(*args, **kwargs):
print(f"appel : args={args}, kwargs={kwargs}")
result = func(*args, **kwargs) # dépaquette et transmet ici aussi
print(f"résultat : {result}")
return result # n'oublie pas de le retourner
return wrapper
@log_call
def add(a, b):
return a + b
print(add(2, 3))
# appel : args=(2, 3), kwargs={}
# résultat : 5
# 5
print(add(2, b=3))
# appel : args=(2,), kwargs={'b': 3}
# résultat : 5
# 5
N'oublie pas de return le résultat
Si wrapper n'écrit que result = func(...) et oublie le return, la valeur de retour de la fonction décorée se transforme silencieusement en None. L'accident classique, c'est de retrouver add(2, 3) qui retourne discrètement None — quand tu écris un décorateur, traite return result comme faisant partie de la même mémoire musculaire.
Vérification des connaissances
Répondez à chaque question une par une.
Question 2Qu'affiche ce code ?def deco(func):
def wrapper(*args, **kwargs):
return func(*args, **kwargs) * 2
return wrapper
@deco
def plus(a, b):
return a + b
print(plus(3, 4))