Quand j'ai commencé à programmer avec Python, si je ne me trompe pas, la version était la 3.3. Par conséquent, lorsque j'ai commencé à programmer, les décorateurs étaient disponibles depuis longtemps pour la communauté Python.
Les décorateurs de fonctions sont arrivés sur Python avec la version 2.2 et les décorateurs de classes sont arrivés sur Python avec la version 2.6.
Personnellement, je considère la fonctionnalité Decorator de Python comme une fonctionnalité très puissante du langage.
En fait, mon objectif est de faire une série d'articles sur les sujets les plus difficiles à comprendre en Python. Je compte aborder ces sujets, qui sont un peu plus d'une dizaine, un par un.
Dans cet article, j'essaierai d'aborder autant que possible chaque partie du sujet des décorateurs.
Essentiellement, un décorateur est un modèle de conception en Python qui vous permet de modifier le comportement d'une fonction ou d'une classe sans changer sa structure de base. Les décorateurs sont une forme de métaprogrammation, dans laquelle vous écrivez essentiellement du code qui manipule d'autres codes.
Vous savez que Python résout les noms en utilisant la portée indiquée dans l'ordre ci-dessous :
Les décorateurs sont assis dans la portée Enclosing, qui est étroitement liée au concept de Closure.
Idée clé : Un décorateur prend une fonction en entrée, y ajoute des fonctionnalités et renvoie une fonction modifiée.
Analogie : Pensez à un décorateur comme à un emballage cadeau. Vous avez un cadeau (la fonction originale) et vous l'enveloppez avec du papier décoratif (le décorateur) pour le rendre plus joli ou ajouter des fonctionnalités supplémentaires (comme un nœud ou une carte). Le cadeau à l'intérieur reste le même, mais sa présentation ou les actions associées sont valorisées.
La plupart des décorateurs en Python sont implémentés à l'aide de fonctions, mais vous pouvez également créer des décorateurs à l'aide de classes.
Les décorateurs basés sur les fonctions sont plus courants et plus simples, tandis que les décorateurs basés sur les classes offrent une flexibilité supplémentaire.
def my_decorator(func): def wrapper(*args, **kwargs): # Do something before calling the decorated function print("Before function call") result = func(*args, **kwargs) # Do something after calling the decorated function print("After function call") return result return wrapper @my_decorator def say_hello(name): print(f"Hello, {name}!") say_hello("World")
Explication :
Ceux-ci utilisent des classes au lieu de fonctions pour définir les décorateurs.
class MyDecorator: def __init__(self, func): self.func = func def __call__(self, *args, **kwargs): # Do something before calling the decorated function print("Before function call") result = self.func(*args, **kwargs) # Do something after calling the decorated function print("After function call") return result @MyDecorator def say_hello(name): print(f"Hello, {name}!") say_hello("World")
Explication :
Le concept fondamental des décorateurs est que ce sont des fonctions qui prennent une autre fonction comme argument et étendent son comportement sans la modifier explicitement.
Voici le formulaire le plus simple :
def my_decorator(func): def wrapper(): print("Something is happening before the function is called.") func() print("Something is happening after the function is called.") return wrapper # Using the decorator with @ syntax @my_decorator def say_hello(): print("Hello!") # When we call say_hello() say_hello() # This is equivalent to: # say_hello = my_decorator(say_hello)
Créons un décorateur qui enregistre le temps d'exécution d'une fonction :
def decorator_with_args(func): def wrapper(*args, **kwargs): # Accept any number of arguments print(f"Arguments received: {args}, {kwargs}") return func(*args, **kwargs) # Pass arguments to the original function return wrapper @decorator_with_args def greet(name, greeting="Hello"): print(f"{greeting}, {name}!") greet("Alice", greeting="Hi") # Prints arguments then "Hi, Alice!"
Ce sont des décorateurs qui peuvent accepter leurs propres paramètres :
def repeat(times): def decorator(func): def wrapper(*args, **kwargs): for _ in range(times): result = func(*args, **kwargs) return result return wrapper return decorator @repeat(times=3) def greet(name): print(f"Hello {name}") return "Done" greet("Bob") # Prints "Hello Bob" three times
def singleton(cls): instances = {} def get_instance(*args, **kwargs): if cls not in instances: instances[cls] = cls(*args, **kwargs) return instances[cls] return get_instance @singleton class DatabaseConnection: def __init__(self): print("Initializing database connection") # Creating multiple instances actually returns the same instance db1 = DatabaseConnection() # Prints initialization db2 = DatabaseConnection() # No initialization printed print(db1 is db2) # True
Ceux-ci sont spécialement conçus pour les méthodes de classe :
def debug_method(func): def wrapper(self, *args, **kwargs): print(f"Calling method {func.__name__} of {self.__class__.__name__}") return func(self, *args, **kwargs) return wrapper class MyClass: @debug_method def my_method(self, x, y): return x + y obj = MyClass() print(obj.my_method(5, 3))
Plusieurs décorateurs peuvent être appliqués à une seule fonction :
def bold(func): def wrapper(): return "<b>" + func() + "</b>" return wrapper def italic(func): def wrapper(): return "<i>" + func() + "</i>" return wrapper @bold @italic def greet(): return "Hello!" print(greet()) # Outputs: <b><i>Hello!</i></b>
Explication :
Le décorateur functools.wraps, Voir la documentation, est une fonction d'assistance qui préserve les métadonnées de la fonction d'origine (comme son nom, sa docstring et sa signature) lorsque vous l'enveloppez avec un décorateur. Si vous ne l'utilisez pas, vous perdrez ces informations importantes.
Exemple :
def my_decorator(func): def wrapper(*args, **kwargs): """Wrapper docstring""" return func(*args, **kwargs) return wrapper @my_decorator def my_function(): """My function docstring""" pass print(my_function.__name__) print(my_function.__doc__)
Sortie :
wrapper Wrapper docstring
Problème :
Solution : utilisez functools.wraps) :
def my_decorator(func): def wrapper(*args, **kwargs): # Do something before calling the decorated function print("Before function call") result = func(*args, **kwargs) # Do something after calling the decorated function print("After function call") return result return wrapper @my_decorator def say_hello(name): print(f"Hello, {name}!") say_hello("World")
Sortie :
class MyDecorator: def __init__(self, func): self.func = func def __call__(self, *args, **kwargs): # Do something before calling the decorated function print("Before function call") result = self.func(*args, **kwargs) # Do something after calling the decorated function print("After function call") return result @MyDecorator def say_hello(name): print(f"Hello, {name}!") say_hello("World")
Les décorateurs peuvent également conserver l’état entre les appels de fonction. Ceci est particulièrement utile pour des scénarios tels que la mise en cache ou le comptage des appels de fonction.
def my_decorator(func): def wrapper(): print("Something is happening before the function is called.") func() print("Something is happening after the function is called.") return wrapper # Using the decorator with @ syntax @my_decorator def say_hello(): print("Hello!") # When we call say_hello() say_hello() # This is equivalent to: # say_hello = my_decorator(say_hello)
Sortie :
def decorator_with_args(func): def wrapper(*args, **kwargs): # Accept any number of arguments print(f"Arguments received: {args}, {kwargs}") return func(*args, **kwargs) # Pass arguments to the original function return wrapper @decorator_with_args def greet(name, greeting="Hello"): print(f"{greeting}, {name}!") greet("Alice", greeting="Hi") # Prints arguments then "Hi, Alice!"
Explication :
La fonction wrapper maintient un compteur (appels) qui s'incrémente à chaque fois que la fonction décorée est appelée.
Ceci est un exemple simple de la façon dont les décorateurs peuvent être utilisés pour maintenir l'état.
Vous pouvez trouver ci-dessous une liste organisée de décorateurs Python :
Les décorateurs sont une fonctionnalité puissante et élégante de Python qui vous permet d'améliorer les fonctions et les classes de manière propre et déclarative.
En comprenant les principes, les meilleures pratiques et les pièges potentiels, vous pouvez exploiter efficacement les décorateurs pour écrire un code plus modulaire, maintenable et expressif.
Ils constituent un outil précieux dans l'arsenal de tout programmeur Python, en particulier lorsqu'il travaille avec des frameworks ou crée des composants réutilisables.
Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!