Bonjour à tous,
Je vous propose un tutoriel introduction aux décorateurs Python.
Bonne lecture
Bonjour à tous,
Je vous propose un tutoriel introduction aux décorateurs Python.
Bonne lecture
Sympa l'article, je dirais que c'est a travers cet genre d'article qu'on se perfctionne. On a souvent une vue differente de ce que l'on avait avant la lecture...
Bon article clair et concis.
Quand j'ai débuté en Python, j'ai dû rapidement programmer des décorateurs sur les seules bases de la doc officielle et un ou l'autre exemple.
Après quelque séances d'arrachage de cheveux, je les ai maîtrisés (sales bêtes ! ).
Ton article devrais préserver l'intégralité capillaire de nos successeurs.
Merci
Bonjour,
article très intéressant.
Pour avoir fait du C je trouve une similitude des décorateurs avec le code préprocesseur, dans la possibilité de modifier la définition d'une fonction juste en passant un "alias" à l'aide de #define qu'on peut mettre sous condition #if #else etc... on gagne énormément en écriture et flexibilité du code.
Pour aller un peu plus loin et essayer de rendre le décorateur le plus discret possible, qu'il ne modifie pas les héritages de la déclaration d'origine :
En reprenant l'exemple du chapitre III.
J'ai rajouté quelques docstring pour l'explication
Si on interroge la fonction ma_fonction sur son propre nom et son docstring :
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 def mon_decorateur(fonction): def ma_fonction_decoree(): """ma fonction décorée """ print(fonction.__name__ + ' appelée') fonction() return ma_fonction_decoree @mon_decorateur def ma_fonction(): """ma fonction affiche hello world """ print("hello_world") if __name__ == "__main__": ma_fonction()
ma_fonction.__name__ va retourner 'ma_fonction_decoree'
De même avec help(ma_fonction)
Chose embêtante si on utilise le même décorateur sur plusieurs fonctions, ça ne facilite pas l'introspection des fonctions.Help on function ma_fonction_decoree in module __main__:
ma_fonction_decoree()
ma fonction décorée
Voici donc comment améliorer ça et rendre le décorateur quasiment transparent pour la fonction décorée :
Méthode 1: Remplacement des attributs manuellement
ma_fonction.__name__ va retourner 'ma_fonction'
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 def mon_decorateur(fonction): def ma_fonction_decoree(): """ma fonction décorée """ print(fonction.__name__ + ' appelée') fonction() ma_fonction_decoree.__name__ = fonction.__name__ ma_fonction_decoree.__doc__ = fonction.__doc__ return ma_fonction_decoree @mon_decorateur def ma_fonction(): """ma fonction affiche hello world """ print("hello_world") if __name__ == "__main__": ma_fonction()
Avec help(ma_fonction)
On peut connaître la liste des attributs à l'aide de la commande : dir(ma_fonction)Help on function ma_fonction in module __main__:
ma_fonction()
ma fonction affiche hello world
Méthode 2: Remplacement des attributs automatiquement
La librairie native functools dispose d'un décorateur (un de plus) wraps qui permet de faire hériter le décorateur des attributs de la fonction d'origine.
L'utilisation est très simple :
__name__ et help fonctionne comme dans la première méthode
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 import functools # Higher-order functions and operations on callable objects def mon_decorateur(fonction): @functools.wraps(fonction) def ma_fonction_decoree(): """ma fonction décorée """ print(fonction.__name__ + ' appelée') fonction() return ma_fonction_decoree @mon_decorateur def ma_fonction(): """ma fonction affiche hello world """ print("hello_world") if __name__ == "__main__": ma_fonction()
En regardant dans la librairie \Lib\functools.py
On réduit donc l'assignement de 5 attributs en une seule ligne. Vive les décorateurs de décorateurs !
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5 # update_wrapper() and wraps() are tools to help write # wrapper functions that can handle naive introspection WRAPPER_ASSIGNMENTS = ('__module__', '__name__', '__qualname__', '__doc__', '__annotations__')
Pour aller plus loint :
Voici juste un exemple de décorateur qui modifie les arguments passés par la fonction d'origine :
Les arguments 1, 2, 3, 4:
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 import functools # Higher-order functions and operations on callable objects def convert_args(*types_args, **kw): def decorator(func): @functools.wraps(func) def newf(*args): """newf""" return func(*(type_arg(arg) for arg, type_arg in zip(args, types_args))) return newf return decorator @convert_args(float, int, str, lambda x:x+1) def function(*args): """Hello """ return [0, ] + list(args) print(function(1, 2, 3, 4)) # [0, 1.0, 2, '3', 5]
1 est converti en float
2 est converti en int, si ce n'était pas le cas
3 est converti en string
4 est incrémenté par la fonction lambda
function retourne une liste avec 0 + les arguments convertis par le décorateur.
C'est vraiment puissant comme concept !
Je vois que l'article plaît, cela fait plaisir.
Merci pour les exemple complémentaire. J'ai appris l'existence de la lib functools. Je tâcherais d'inclure tout cela dans une future mise à jour
Vous avez un bloqueur de publicités installé.
Le Club Developpez.com n'affiche que des publicités IT, discrètes et non intrusives.
Afin que nous puissions continuer à vous fournir gratuitement du contenu de qualité, merci de nous soutenir en désactivant votre bloqueur de publicités sur Developpez.com.
Partager