Lorsque vous utilisez un décorateur, vous remplacez une fonction par une autre. En d'autres termes, si vous avez un décorateur
def logged(func):
def with_logging(*args, **kwargs):
print(func.__name__ + " was called")
return func(*args, **kwargs)
return with_logging
alors quand tu dis
@logged
def f(x):
"""does some math"""
return x + x * x
c'est exactement la même chose que de dire
def f(x):
"""does some math"""
return x + x * x
f = logged(f)
et votre fonction f
est remplacée par la fonction with_logging
. Malheureusement, cela signifie que si vous dites
print(f.__name__)
il s'imprimera with_logging
car c'est le nom de votre nouvelle fonction. En fait, si vous regardez la docstring f
, elle sera vide car with_logging
n'a pas de docstring, et donc la docstring que vous avez écrite ne sera plus là. De plus, si vous regardez le résultat pydoc pour cette fonction, il ne sera pas répertorié comme prenant un seul argument x
; au lieu de cela, il sera répertorié comme prenant *args
et **kwargs
parce que c'est ce que prend with_logging.
Si utiliser un décorateur signifiait toujours perdre ces informations sur une fonction, ce serait un problème grave. C'est pourquoi nous l'avons fait functools.wraps
. Cela prend une fonction utilisée dans un décorateur et ajoute la fonctionnalité de copie sur le nom de la fonction, la docstring, la liste des arguments, etc. Et comme wraps
c'est lui-même un décorateur, le code suivant fait la bonne chose:
from functools import wraps
def logged(func):
@wraps(func)
def with_logging(*args, **kwargs):
print(func.__name__ + " was called")
return func(*args, **kwargs)
return with_logging
@logged
def f(x):
"""does some math"""
return x + x * x
print(f.__name__) # prints 'f'
print(f.__doc__) # prints 'does some math'