Pourquoi les décorateurs Python ?
Les décorateurs en Python sont une fonctionnalité puissante et polyvalente qui permet d'ajouter des comportements supplémentaires à des fonctions, des méthodes ou des classes. Ils sont largement utilisés dans la communauté Python pour augmenter la flexibilité et la productivité du code.
- Contexte réel : Les développeurs Python les utilisent fréquemment dans divers cas tels que le logging, le timing des fonctions, l'authentification, l'introspection, et bien plus encore. Ils permettent d'ajouter des fonctionnalités sans modifier le code original de la fonction.
- Un cas d'utilisation concret : Imaginez une application web où vous voulez mesurer le temps d'exécution de chaque requête pour optimiser les performances. Les décorateurs vous permettent de mesurer ce temps simplement et efficacement, sans avoir à modifier chaque fonction individuellement.
Prerequis
- Connaissances en Python avancées
- Fonctions et méthodes
- Arguments et paramètres
- Exceptions
- Outils à installer :
- Python (v3.6 ou plus récent)
- IDE ou éditeur de code préféré (VSCode, PyCharm, etc.)
Concepts fondamentaux
Décorateur de base
Un décorateur est une fonction qui prend une autre fonction en argument et renvoie une nouvelle fonction avec des comportements supplémentaires.
def ma_fonction():
print("Hello, World!")
##
def mon_decorateur(fonction):
def wrapper():
print("Avant l'appel")
fonction()
print("Après l'appel")
return wrapper
ma_fonction = mon_decorateur(ma_fonction)
ma_fonction()
Utiliser un décorateur avec la syntaxe simplifiée
Python offre une syntaxe plus concise pour appliquer des décorateurs :
def ma_fonction():
print("Hello, World!")
##
@mon_decorateur
def ma_fonction():
print("Hello, World!")
ma_fonction()
Décorateur avec arguments
Les décorateurs peuvent également accepter des arguments. Pour cela, on utilise une fonction interne qui prend un nombre indéfini d'arguments.
def mon_decorateur(avec_argument):
def decorateur(fonction):
def wrapper(*args, **kwargs):
print(f"Avant l'appel avec argument : {avec_argument}")
fonction(*args, **kwargs)
print("Après l'appel")
return wrapper
return decorateur
@mon_decorateur(42)
def ma_fonction():
print("Hello, World!")
ma_fonction()
Multiple décorateurs
On peut appliquer plusieurs décorateurs à une fonction en les chaînant :
def decorateur1(fonction):
def wrapper():
print("Decorator 1 avant")
fonction()
print("Decorator 1 après")
return wrapper
def decorateur2(fonction):
def wrapper():
print("Decorator 2 avant")
fonction()
print("Decorator 2 après")
return wrapper
@decorateur1
@decorateur2
def ma_fonction():
print("Hello, World!")
ma_fonction()
Mise en pratique : projet fil rouge
Dans ce projet fil rouge, nous allons créer un simple gestionnaire de tâches avec des fonctionnalités comme l'ajout, la suppression et la affichage des tâches. Nous utiliserons les décorateurs pour ajouter des comportements supplémentaires, comme le logging.
Étape 1 : Structure du projet
Créez un nouveau dossier gestionnaire_taches et insérez-y les fichiers suivants :
gestionnaire_taches/
├── main.py
├── tache.py
└── utils.py
Étape 2 : Définition des classes
Dans tache.py, définissons la classe Tache pour représenter une tâche.
class Tache:
def __init__(self, description):
self.description = description
self.terminee = False
def terminer(self):
self.terminee = True
Étape 3 : Définition des décorateurs
Dans utils.py, définissons les décorateurs que nous utiliserons.
import logging
logging.basicConfig(level=logging.INFO)
def log_execution(fonction):
def wrapper(*args, **kwargs):
logging.info(f"Appel à {fonction.__name__} avec arguments {args} et keyword arguments {kwargs}")
result = fonction(*args, **kwargs)
logging.info(f"{fonction.__name__} a retourné {result}")
return result
return wrapper
def verif_acces(fonction):
def wrapper(*args, **kwargs):
if not hasattr(args[0], 'est_admin'):
raise Exception("Accès refusé")
return fonction(*args, **kwargs)
return wrapper
Étape 4 : Implémentation des fonctionnalités
Dans main.py, implémentons les fonctionnalités du gestionnaire de tâches.
from tache import Tache
import logging
logging.basicConfig(level=logging.INFO)
class GestionnaireTaches:
def __init__(self):
self.taches = []
@log_execution
def ajouter_tache(self, description):
nouvelle_tache = Tache(description)
self.taches.append(nouvelle_tache)
return nouvelle_tache
@log_execution
@verif_acces
def supprimer_tache(self, index):
if 0 <= index < len(self.taches):
del self.taches[index]
logging.info("Tâche supprimée avec succès")
else:
raise IndexError("Index de tâche invalide")
@log_execution
def afficher_taches(self):
for i, tache in enumerate(self.taches):
print(f"{i}: {tache.description} (Terminée: {'Oui' if tache.terminee else 'Non'})")
Étape 5 : Utilisation du gestionnaire de tâches
Ajoutez le code suivant à main.py pour tester les fonctionnalités.
if __name__ == "__main__":
g = GestionnaireTaches()
try:
tache1 = g.ajouter_tache("Faire les courses")
tache2 = g.ajouter_tache("Nettoyer la maison")
g.afficher_taches()
g.supprimer_tache(0)
g.afficher_taches()
except Exception as e:
print(e)
Étape 6 : Exécution du code
Exécutez le script main.py en utilisant la commande suivante :
python main.py
Vous devriez voir des messages de logging indiquant l'exécution et les résultats des opérations.
Erreurs fréquentes et debugging
1. Erreur : Fonctionnalité manquante
Code incorrect :
def ma_fonction():
print("Hello, World!")
Code correct :
@mon_decorateur
def ma_fonction():
print("Hello, World!")
2. Erreur : Decorator not callable
Code incorrect :
@mon_decorateur(42)
def ma_fonction():
pass
Code correct :
def mon_decorateur(avec_argument):
def decorateur(fonction):
def wrapper(*args, **kwargs):
print(f"Avant l'appel avec argument : {avec_argument}")
fonction(*args, **kwargs)
print("Après l'appel")
return wrapper
return decorateur
@mon_decorateur(42)
def ma_fonction():
pass
3. Erreur : Multiple decorators not working
Code incorrect :
@decorateur1
@decorateur2
def ma_fonction():
print("Hello, World!")
Code correct :
Vérifiez que les décorateurs sont bien définis et qu'ils ne présentent pas d'erreurs syntaxiques.
Pour aller plus loin
- Méta-programmation avancée avec
inspect: Explorez comment utiliser le moduleinspectpour obtenir des informations sur les fonctions. - Decorators for classes and methods : Apprenez à créer et utiliser des décorateurs pour les méthodes et les classes.
- Asynchronous decorators : Découvrez comment créer des décorateurs asynchrones pour gérer des opérations non bloquantes.
Défi pratique
Créez un simple API de blog avec des fonctionnalités comme la création, la lecture et la suppression d'articles. Utilisez les décorateurs pour ajouter des comportements supplémentaires comme le logging et l'autorisation.