функции является стандартным модулем Python для функций более высокого порядка (функций, которые действуют или возвращают другие функции). wraps () — это декоратор, который применяется к функции обертки декоратора. Он обновляет функцию-оболочку, чтобы она выглядела как обернутая функция, копируя такие атрибуты, как __name__, __doc__ (строка документа) и т.д.
Синтаксис:
@functools.wraps(wrapped, assigned = WRAPPER_ASSIGNMENTS, updated = WRAPPER_UPDATES)
Параметры:
wrapped : Имя функции, которое должно быть оформлено функцией-оболочкой.
assigned : Кортеж, чтобы указать, какие атрибуты исходной функции назначаются непосредственно соответствующим атрибутам функции-оболочки. По умолчанию используется WRAPPER_ASSIGNMENTS (который присваивает обертка функции __module__, __name__, __qualname__, __annotations__ and __doc__, документацию строку)
updated : Кортеж, чтобы указать, какие атрибуты функции-оболочки обновляются соответствующими атрибутами исходной функции. По умолчанию установлено значение WRAPPER_UPDATES (которое обновляет __dict__ функции-оболочки, т. е. словарь экземпляра).
Пример 1:
Без functools.wraps()
def a_decorator(func):
def wrapper(*args, **kwargs):
"""A wrapper function"""
# Extend some capabilities of func
func()
return wrapper
@a_decorator
def first_function():
"""This is docstring for first function"""
print("first function")
@a_decorator
def second_function(a):
"""This is docstring for second function"""
print("second function")
print(first_function.__name__)
print(first_function.__doc__)
print(second_function.__name__)
print(second_function.__doc__)
Выход:
wrapper
A wrapper function
wrapper
A wrapper function
Теперь, что произойдет, если мы напишем help(first_function) и help(second_function)
print("First Function")
help(first_function)
print("\nSecond Function")
help(second_function)
Выход:
First Function
Help on function wrapper in module __main__:
wrapper(*args, **kwargs)
A wrapper function
Second Function
Help on function wrapper in module __main__:
wrapper(*args, **kwargs)
A wrapper function
Хотя приведенный выше код будет работать логически нормально, но учтите это, если вы пишете API или библиотеку, и кто-то хочет знать, что делает ваша функция, и ее имя или просто введите справку(ваша функция), он всегда будет показывать имя функции-оболочки и строку документа. Это становится еще более запутанным, если вы использовали одну и ту же функцию-оболочку для разных функций, так как она будет отображать одни и те же сведения для каждой из них.
В идеале он должен показывать имя и строку документа обернутой функции вместо функции обертывания. Ручным решением было бы назначить __name__, __doc__ атрибуты в функции переноса перед ее возвратом.
def a_decorator(func):
def wrapper(*args, **kwargs):
"""A wrapper function"""
# Extend some capabilities of func
func()
wrapper.__name__ = func.__name__
wrapper.__doc__ = func.__doc__
return wrapper
@a_decorator
def first_function():
"""This is docstring for first function"""
print("first function")
@a_decorator
def second_function(a):
"""This is docstring for second function"""
print("second function")
print(first_function.__name__)
print(first_function.__doc__)
print(second_function.__name__)
print(second_function.__doc__)
Выход:
first_function
This is docstring for first function
second_function
This is docstring for second function
Это решает проблему, но что, если мы снова введем справку(ваша функция),
print("First Function")
help(first_function)
print("\nSecond Function")
help(second_function)
Для first_function: справка(first_function)
Выход:
First Function
Help on function first_function in module __main__:
first_function(*args, **kwargs)
This is docstring for first function
Second Function
Help on function second_function in module __main__:
second_function(*args, **kwargs)
This is docstring for second function
Как вы можете видеть, у него все еще есть проблема, т. Е. подпись функции, она показывает подпись, используемую функцией-оболочкой (здесь, общая подпись) для каждого из них. Кроме того, если вы реализуете много декораторов, то вам нужно написать эти строки для каждого из них.
Поэтому, чтобы сэкономить время и повысить читабельность, мы могли бы использовать functools.wraps() as decorator to wrapper function.
Пример (with functools.wraps())
from functools import wraps
def a_decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
"""A wrapper function"""
# Extend some capabilities of func
func()
return wrapper
@a_decorator
def first_function():
"""This is docstring for first function"""
print("first function")
@a_decorator
def second_function(a):
"""This is docstring for second function"""
print("second function")
print(first_function.__name__)
print(first_function.__doc__)
print(second_function.__name__)
print(second_function.__doc__)
Выход:
first_function
This is docstring for first function
second_function
This is docstring for second function
Теперь, если мы введем справку(first_function):
print("First Function")
help(first_function)
print("\nSecond Function")
help(second_function)
Выход:
First Function
Help on function first_function in module __main__:
first_function()
This is docstring for first function
Second Function
Help on function second_function in module __main__:
second_function(a)
This is docstring for second function