Introduction
Les décorateurs sont une fonctionnalité puissante et élégante en Python, permettant de modifier ou d'enrichir les fonctions et les méthodes sans en altérer le code source. Ils sont souvent utilisés pour ajouter des fonctionnalités, telles que la journalisation, la gestion des erreurs, ou le contrôle des accès, sans modifier directement le code de la fonction décorée. Les décorateurs permettent donc de structurer et modulariser votre code de manière plus flexible et lisible.
Dans cet article, nous allons découvrir le fonctionnement des décorateurs en Python, leur syntaxe, et quelques exemples pratiques d’utilisation.
1. Qu'est-ce qu'un décorateur en Python ?
Un décorateur est une fonction qui prend une autre fonction en argument, la modifie ou lui ajoute des fonctionnalités, puis retourne une nouvelle fonction enrichie. Le décorateur est appliqué à une fonction en utilisant le symbole @
avant la définition de la fonction.
Syntaxe :
@decorateur
def fonction_a_decorer():
pass
Cela équivaut à écrire :
fonction_a_decorer = decorateur(fonction_a_decorer)
Exemple simple de décorateur :
def mon_decorateur(fonction):
def fonction_modifiee():
print("Avant l'appel de la fonction")
fonction()
print("Après l'appel de la fonction")
return fonction_modifiee
@mon_decorateur
def saluer():
print("Bonjour !")
saluer()
# Affiche :
# Avant l'appel de la fonction
# Bonjour !
# Après l'appel de la fonction
Dans cet exemple, le décorateur mon_decorateur
ajoute du code avant et après l'exécution de la fonction saluer
.
2. Créer des décorateurs en Python
Un décorateur en Python prend une fonction comme argument et retourne une nouvelle fonction, qui étend le comportement de la fonction d’origine.
Étapes pour créer un décorateur :
- Définissez une fonction décorateur qui prend une fonction en argument.
- Définissez une fonction interne qui ajoute les fonctionnalités souhaitées autour de la fonction d'origine.
- Retournez la fonction interne.
Exemple : Décorateur pour calculer le temps d'exécution d'une fonction
import time
def mesure_temps(fonction):
def wrapper():
debut = time.time()
fonction()
fin = time.time()
print(f"Temps d'exécution : {fin - debut} secondes")
return wrapper
@mesure_temps
def somme_grande_liste():
print(sum(range(1000000)))
somme_grande_liste()
Ce décorateur, mesure_temps
, mesure et affiche le temps nécessaire pour exécuter la fonction somme_grande_liste
.
3. Utiliser des décorateurs avec des fonctions à arguments
Pour créer un décorateur qui fonctionne avec des fonctions ayant des arguments, utilisez *args
et **kwargs
pour accepter tous les types d’arguments.
Exemple : Décorateur avec arguments
def decorateur_arguments(fonction):
def wrapper(*args, **kwargs):
print("Arguments :", args, kwargs)
return fonction(*args, **kwargs)
return wrapper
@decorateur_arguments
def addition(a, b):
return a + b
print(addition(3, 5)) # Affiche "Arguments : (3, 5) {}" puis "8"
Dans cet exemple, le décorateur decorateur_arguments
affiche les arguments de la fonction addition
avant de l’exécuter.
4. Utiliser plusieurs décorateurs
Il est possible d’utiliser plusieurs décorateurs sur une seule fonction. Python les applique de haut en bas, dans l'ordre où ils sont déclarés.
Exemple avec plusieurs décorateurs
def decorateur_1(fonction):
def wrapper(*args, **kwargs):
print("Décorateur 1")
return fonction(*args, **kwargs)
return wrapper
def decorateur_2(fonction):
def wrapper(*args, **kwargs):
print("Décorateur 2")
return fonction(*args, **kwargs)
return wrapper
@decorateur_1
@decorateur_2
def afficher():
print("Fonction exécutée")
afficher()
# Affiche :
# Décorateur 1
# Décorateur 2
# Fonction exécutée
Dans cet exemple, les deux décorateurs sont appliqués en séquence avant l'exécution de la fonction afficher
.
5. Décorateurs avec arguments
Les décorateurs peuvent eux-mêmes accepter des arguments pour personnaliser leur comportement.
Exemple : Décorateur avec argument
def repeter(n):
def decorateur(fonction):
def wrapper(*args, **kwargs):
for _ in range(n):
fonction(*args, **kwargs)
return wrapper
return decorateur
@repeter(3)
def saluer():
print("Bonjour !")
saluer()
# Affiche "Bonjour !" trois fois
Le décorateur repeter
prend un argument n
et crée un décorateur qui répète l’appel de la fonction saluer
trois fois.
6. Décorateurs intégrés en Python
Python inclut plusieurs décorateurs intégrés pour des cas d’utilisation courants, comme :
@staticmethod
: Pour définir des méthodes statiques, qui n’accèdent pas aux attributs ou méthodes de l’instance.@classmethod
: Pour définir des méthodes de classe, qui prennent la classe elle-même comme premier argument.@property
: Pour transformer une méthode en attribut accessible en lecture seule.
Exemple avec @property
class Cercle:
def __init__(self, rayon):
self._rayon = rayon
@property
def rayon(self):
return self._rayon
@rayon.setter
def rayon(self, valeur):
if valeur > 0:
self._rayon = valeur
else:
print("Le rayon doit être positif")
# Utilisation
cercle = Cercle(5)
print(cercle.rayon) # Affiche 5
cercle.rayon = 10 # Modifie le rayon
print(cercle.rayon) # Affiche 10
Dans cet exemple, le décorateur @property
permet d’accéder à rayon
comme un attribut tout en maintenant un contrôle sur la modification de sa valeur.
7. Avantages des décorateurs
Lisibilité et modularité
Les décorateurs permettent de séparer des fonctionnalités transversales (comme la journalisation ou le contrôle des accès) du code principal, ce qui rend le code plus lisible et plus modulaire.
Réutilisabilité
Les décorateurs peuvent être appliqués à plusieurs fonctions, ce qui permet de réutiliser du code sans duplication.
Facilité de maintenance
Les fonctionnalités additionnelles peuvent être modifiées dans le décorateur sans altérer les fonctions décorées, facilitant ainsi la maintenance du code.
Conclusion
Les décorateurs en Python sont un outil puissant pour modifier ou étendre les fonctionnalités des fonctions et méthodes sans en altérer le code source. Avec une utilisation judicieuse, ils permettent de rendre le code plus propre, modulaire et réutilisable. Qu'il s'agisse de journalisation, de contrôle d'accès ou d'optimisation des performances, les décorateurs sont un excellent moyen de structurer des fonctionnalités additionnelles dans vos programmes Python.