Les décorateurs en Python
Cet article explique les décorateurs en Python.
YouTube Video
Les décorateurs en Python
Les décorateurs en Python sont une fonctionnalité puissante permettant d'ajouter des fonctionnalités supplémentaires aux fonctions ou aux méthodes. Les décorateurs permettent d'ajouter de nouvelles fonctionnalités sans modifier le code existant, améliorant ainsi sa réutilisabilité et sa maintenabilité.
Principes de base des décorateurs
Les décorateurs en Python fonctionnent en prenant une fonction en argument et en renvoyant une nouvelle fonction dotée de fonctionnalités supplémentaires. Avec les décorateurs, vous pouvez facilement ajouter un pré-traitement ou un post-traitement aux fonctions.
Sans les décorateurs, modifier le comportement d'une fonction nécessite de modifier directement la fonction elle-même. Avec les décorateurs, vous pouvez étendre les fonctionnalités sans changer l'implémentation originale.
Structure de base d'un décorateur
1def my_decorator(func):
2 def wrapper():
3 print("Before the function call")
4 func()
5 print("After the function call")
6 return wrapper
7
8@my_decorator
9def say_hello():
10 print("Hello!")
11
12say_hello()
Dans cet exemple, la fonction my_decorator
sert de décorateur en encapsulant la fonction say_hello
. Le décorateur est appliqué à l'aide de la syntaxe @my_decorator
, et lorsque say_hello()
est appelé, le pré- et post-traitement fourni par le décorateur est automatiquement exécuté.
Comment fonctionnent les décorateurs
Les décorateurs fonctionnent selon les étapes suivantes.
-
Le décorateur prend une fonction (ou une méthode) comme argument.
-
Il définit une fonction intermédiaire pour exécuter la fonction originale.
-
La fonction intermédiaire effectue un traitement supplémentaire avant ou après l'exécution de la fonction originale.
-
Le décorateur retourne la fonction intermédiaire. En conséquence, la fonction originale est remplacée par une nouvelle fonction décorée.
Décorateurs pour les fonctions avec des arguments
Lors de l'application d'un décorateur à une fonction avec des arguments, la fonction intermédiaire doit être adaptée pour accepter ces arguments.
1def my_decorator(func):
2 def wrapper(*args, **kwargs):
3 print("Before the function call")
4 result = func(*args, **kwargs)
5 print("After the function call")
6 return result
7 return wrapper
8
9@my_decorator
10def greet(name):
11 print(f"Hello, {name}!")
12
13greet("Alice")
En utilisant *args
et **kwargs
, il est possible de gérer des fonctions acceptant un nombre quelconque d'arguments et de paramètres nommés. Cela permet d'appliquer des décorateurs de manière générique à des fonctions avec tout type d'arguments.
Exemples d'application des décorateurs
Décorateur pour la journalisation
Les décorateurs sont souvent utilisés pour ajouter des fonctionnalités de journalisation. Par exemple, en créant un décorateur qui journalise avant et après l'exécution d'une fonction, vous pouvez enregistrer quand la fonction est appelée et combien de temps elle met à s'exécuter.
1import time
2
3def log_time(func):
4 def wrapper(*args, **kwargs):
5 start_time = time.time()
6 print(f"Calling function '{func.__name__}'...")
7 result = func(*args, **kwargs)
8 end_time = time.time()
9 print(f"Function '{func.__name__}' completed in {end_time - start_time} seconds")
10 return result
11 return wrapper
12
13@log_time
14def long_task(duration):
15 time.sleep(duration)
16 print("Task completed!")
17
18long_task(2)
Dans cet exemple, le décorateur log_time
mesure le temps d'exécution d'une fonction et l'affiche sous forme de journal. La fonction long_task
est enveloppée par le décorateur et son temps d'exécution est enregistré pendant l'exécution.
Décorateur pour la gestion des permissions
Les décorateurs peuvent également être utilisés pour la gestion des permissions. Par exemple, vous pouvez restreindre un traitement en vérifiant si un utilisateur dispose de permissions spécifiques.
1def requires_permission(user_role):
2 def decorator(func):
3 def wrapper(*args, **kwargs):
4 if user_role != "admin":
5 print("Permission denied!")
6 return
7 return func(*args, **kwargs)
8 return wrapper
9 return decorator
10
11@requires_permission("admin")
12def delete_user(user_id):
13 print(f"User {user_id} has been deleted.")
14
15delete_user(123) # Executed
16delete_user = requires_permission("guest")(delete_user)
17delete_user(456) # Permission denied!
Le décorateur requires_permission
restreint l'exécution d'une fonction en fonction du rôle d'un utilisateur. En regroupant cette logique dans un décorateur, vous pouvez éviter que la gestion des permissions soit dispersée dans votre code, améliorant ainsi sa lisibilité.
Imbriquer des décorateurs
Il est possible d'appliquer plusieurs décorateurs. Lorsque plusieurs décorateurs sont appliqués à une seule fonction, ils s'exécutent dans l'ordre, de haut en bas.
1def uppercase(func):
2 def wrapper(*args, **kwargs):
3 result = func(*args, **kwargs)
4 return result.upper()
5 return wrapper
6
7def exclaim(func):
8 def wrapper(*args, **kwargs):
9 result = func(*args, **kwargs)
10 return result + "!"
11 return wrapper
12
13@uppercase
14@exclaim
15def greet(name):
16 return f"Hello, {name}"
17
18print(greet("Alice")) # "HELLO, ALICE!"
Dans cet exemple, deux décorateurs sont appliqués à la fonction greet
. Le décorateur @exclaim
ajoute un point d'exclamation à la fin de la chaîne, puis @uppercase
convertit la chaîne en majuscules.
Décorateurs pour les méthodes de classe
Les décorateurs peuvent également être utilisés avec des méthodes de classe. Cela est particulièrement utile lorsque vous souhaitez contrôler le comportement des méthodes au sein d'une classe. Lorsque vous appliquez des décorateurs aux méthodes de classe, vous devez faire attention aux arguments comme self
ou cls
.
1def log_method_call(func):
2 def wrapper(self, *args, **kwargs):
3 print(f"Calling method '{func.__name__}'...")
4 return func(self, *args, **kwargs)
5 return wrapper
6
7class MyClass:
8 @log_method_call
9 def greet(self, name):
10 print(f"Hello, {name}")
11
12obj = MyClass()
13obj.greet("Bob")
Dans cet exemple, le décorateur log_method_call
est appliqué à la méthode greet
pour afficher un journal lorsque la méthode est appelée.
Précautions avec les décorateurs
Il y a certaines considérations à prendre en compte lors de l'utilisation de décorateurs. Les décorateurs peuvent potentiellement modifier le nom et la chaîne de documentation de la fonction originale (par exemple, __name__
ou __doc__
), il est donc recommandé d'utiliser functools.wraps
pour préserver ces informations.
1import functools
2
3def my_decorator(func):
4 @functools.wraps(func)
5 def wrapper(*args, **kwargs):
6 print("Before the function call")
7 return func(*args, **kwargs)
8 return wrapper
Utiliser @functools.wraps
garantit que les métadonnées de la fonction originale sont correctement transférées à la fonction enveloppe.
Résumé
Les décorateurs Python sont un outil très puissant qui permet d'ajouter de manière concise des fonctionnalités supplémentaires aux fonctions et aux méthodes. Ils peuvent être utilisés dans divers scénarios pour réduire la duplication de code et améliorer la maintenabilité ainsi que la réutilisabilité. En comprenant le fonctionnement des décorateurs, vous pouvez écrire un code plus efficace et flexible.
Vous pouvez suivre l'article ci-dessus avec Visual Studio Code sur notre chaîne YouTube. Veuillez également consulter la chaîne YouTube.