Как Python понимает в какую функцию в декораторе передавать функцию обернутую ранее декоратором
def outer_decorator(arg1, arg2):
def middle_decorator(func):
def inner_decorator(*args, **kwargs):
print(f"Аргументы для внешнего декоратора: {arg1}, {arg2}")
print("Дополнительные действия перед вызовом функции")
result = func(*args, **kwargs)
print("Дополнительные действия после вызова функции")
return result
return inner_decorator
return middle_decorator
@outer_decorator("значение1", "значение2")
def my_function():
print("Оригинальная функция")
my_function()
Как питон понимает в какую функцию передавать аргумент (в виде функции my_function
) в функции (декораторе) outer_decorator
, ведь в сам outer_decorator
мы передаём лишь два аргумента arg1, arg2
, и мы не передаём саму функцию: my_function
? Но так или иначе мы можем получить её(my_function
) в middle_decorator
, хоть мы её никак не передаём и по сути не знаем.
В этом всем и заключается вопрос "как?" :)
Ответы (1 шт):
Добавление обычного декоратора эквивалентно такому коду:
func = decorator(func)
Добавление параметризованного декоратора эквивалентно сначала вызову внешнего декоратора как функции с указанными параметрами, потом вызова его результата с передачей функции как параметра, на вашем примере это будет выглядеть так:
my_function = outer_decorator("значение1", "значение2")(my_function)
или если расписать в два этапа:
decorator = outer_decorator("значение1", "значение2")
my_function = decorator(my_function)
То есть, сначала вызывается внешний декоратор, из него возвращается middle_decorator, он тоже вызывается с передачей в него функции, из него возвращается внутренняя функция-обертка (то что у вас называется inner_decorator), и она перезаписывает исходную функцию.
Если добавить больше print-ов, будет видно, что все выполняется именно в таком порядке. Имена немного поменял.
def parametrized_decorator(arg1, arg2):
print(f"Вызов parametrized_decorator c параметрами {arg1}, {arg2}")
def decorator(func):
print(f"Вызов decorator с параметром {func}")
def wrapper(*args, **kwargs):
print("Вызов wrapper")
print(f"Аргументы для внешнего декоратора: {arg1}, {arg2}")
print("Дополнительные действия перед вызовом функции")
result = func(*args, **kwargs)
print("Дополнительные действия после вызова функции")
return result
print("Возвращаем wrapper из decorator")
return wrapper
print("Возвращаем decorator из parametrized_decorator")
return decorator
@parametrized_decorator("значение1", "значение2")
def my_function():
print("Оригинальная функция")
my_function()
Вывод:
Вызов parametrized_decorator c параметрами значение1, значение2
Возвращаем decorator из parametrized_decorator
Вызов decorator с параметром <function my_function at 0x7fab047c7e20>
Возвращаем wrapper из decorator
Вызов wrapper
Аргументы для внешнего декоратора: значение1, значение2
Дополнительные действия перед вызовом функции
Оригинальная функция
Дополнительные действия после вызова функции
При этом вложенные функции создаются в момент вызова внешней функции, и имеют доступ к локальным переменным (в том числе и параметрам) внешней функции на момент этого вызова. И самая внутренняя функция будет знать о параметрах внешних функций.