2.23. Décorateurs¶
Un décorateur est un élément de syntaxe qui enveloppe une fonction (ou une méthode) dans une autre fonction. L’enveloppe peut ajouter un comportement avant et après l’appel d’origine, remplacer la valeur de retour ou attacher des métadonnées. La forme est :
@wrapper
def f():
...
La ligne @wrapper est équivalente à f = wrapper(f) – la fonction f est construite normalement, puis transmise à wrapper, et le résultat est réassocié au nom f.
Un décorateur prend une fonction en entrée et renvoie une nouvelle fonction.¶
2.23.1. Décorateurs de méthodes intégrés¶
Quelques décorateurs sont fournis avec Python et s’utilisent à l’intérieur des corps de classe.
2.23.1.1. @property¶
Transforme une méthode en attribut calculé. L’appelant y accède comme s’il s’agissait d’un simple attribut (sans parenthèses), mais une méthode s’exécute à chaque lecture :
class Circle:
def __init__(self, radius):
self.radius = radius
@property
def area(self):
return 3.14159 * self.radius * self.radius
c = Circle(5)
print(c.area) # 78.53975 -- no parentheses
Utilisez-le lorsqu’un attribut qui semble simple nécessite un petit calcul en coulisses. Si le travail est coûteux, préférez une méthode ordinaire – les appelants ne s’attendent pas à ce que la lecture d’un attribut soit lente.
2.23.1.2. @classmethod¶
Définit une méthode qui reçoit la classe comme premier argument au lieu d’une instance. Le premier paramètre est conventionnellement nommé cls :
class Point:
def __init__(self, x, y):
self.x = x
self.y = y
@classmethod
def origin(cls):
return cls(0, 0)
p = Point.origin()
Les méthodes de classe sont la manière standard de fournir des constructeurs alternatifs – des fonctions fabriques qui renvoient une instance construite d’une façon non standard.
2.23.1.3. @staticmethod¶
Définit une méthode qui ne reçoit ni l’instance ni la classe – ce n’est qu’une simple fonction qui réside dans l’espace de noms de la classe pour des raisons d’organisation :
class Temperature:
@staticmethod
def c_to_f(c):
return c * 9 / 5 + 32
Temperature.c_to_f(100) # 212.0
Utilisez-le avec parcimonie ; si une fonction n’a vraiment rien à voir avec l’état de la classe, une simple fonction au niveau du module est généralement plus propre.
2.23.2. Écrire un décorateur personnalisé¶
Un décorateur est une fonction qui prend une fonction et renvoie une fonction. La forme minimale :
def log_calls(func):
def wrapper(*args, **kwargs):
print("calling", func.__name__)
return func(*args, **kwargs)
return wrapper
@log_calls
def add(a, b):
return a + b
add(2, 3)
Sortie
calling add
Le wrapper capture func par fermeture et lui transmet tout. *args / **kwargs lui permet de fonctionner sur n’importe quelle fonction, pas seulement celles à deux arguments. Ce modèle est le fondement de décorateurs plus élaborés (chronométrage, mise en cache, nouvelle tentative en cas d’échec) mais le cœur reste toujours le même : prendre une fonction en entrée, renvoyer une fonction en sortie.