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
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() |
Si on interroge la fonction ma_fonction sur son propre nom et son docstring :
ma_fonction.__name__ va retourner
'ma_fonction_decoree'De même avec
help(ma_fonction)
Help on function ma_fonction_decoree in module __main__:
ma_fonction_decoree()
ma fonction décorée
Chose embêtante si on utilise le même décorateur sur plusieurs fonctions, ça ne facilite pas l'introspection des fonctions.
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
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() |
ma_fonction.__name__ va retourner
'ma_fonction'Avec
help(ma_fonction)
Help on function ma_fonction in module __main__:
ma_fonction()
ma fonction affiche hello world
On peut connaître la liste des attributs à l'aide de la commande :
dir(ma_fonction)Méthode 2: Remplacement des attributs automatiquementLa 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 :
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() |
__name__ et help fonctionne comme dans la première méthode
En regardant dans la librairie \Lib\functools.py
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__') |
On réduit donc l'assignement de 5 attributs en une seule ligne. Vive les décorateurs de décorateurs !
Pour aller plus loint :
Voici juste un exemple de décorateur qui modifie les arguments passés par la fonction d'origine :
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] |
Les arguments 1, 2, 3, 4:
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 !
3 |
0 |