Когда вы используете декоратор, вы заменяете одну функцию другой. Другими словами, если у вас есть декоратор
def logged(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
это то же самое, что сказать
def f(x):
"""does some math"""
return x + x * x
f = logged(f)
и ваша функция f
заменяется функцией with_logging. К сожалению, это означает, что если вы скажете
print(f.__name__)
будет напечатано with_logging
, потому что это имя вашей новой функции. Фактически, если вы посмотрите на строку документации для f
, она будет пустой, потому что with_logging
не имеет строки документации, и поэтому записанная вами строка документации больше не будет там. Кроме того, если вы посмотрите на результат pydoc для этой функции, он не будет указан как принимающий один аргумент x
; вместо этого он будет указан как *args
и **kwargs
, потому что это то, что занимает with_logging.
Если использование декоратора всегда означало потерю этой информации о функции, это было бы серьезной проблемой. Вот почему у нас functools.wraps
. Это берет функцию, используемую в декораторе, и добавляет функцию копирования по имени функции, строке документации, списку аргументов и т. Д. А поскольку wraps
сам является декоратором, следующий код делает правильную вещь:
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'