You are currently viewing Python | functools.wraps() function

Python | functools.wraps() function

функции является стандартным модулем 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