#python #python-3.x #recursion #closures #decorator
#python #python-3.x #рекурсия #замыкания #декоратор
Вопрос:
почему рекурсивный func(n)
в строке 4 вызывает inner
функцию n раз, а не только исходную функцию fact
.
def log(func):
def inner(n):
print('inner()')
result = func(n)
return result
return inner
def fact(n):
return 1 if n < 2 else n*fact(n-1)
fact = log(fact)
if __name__ == '__main__':
fact(4)
Вывод, что меня смутило (ожидаемая печать выполняется только один раз):
inner()
inner()
inner()
inner()
В строке fact = log(fact)
декоратор log
возвращает ссылку на inner
объект функции. Эта ссылка присваивается name fact
и fact.__name__
теперь печатается inner
. Кроме того, когда я проверяю id(fact)
, она возвращает новое значение.
Внутри оформленной fact
функции свободная переменная func
по-прежнему указывает на исходный fact
объект функции. Я могу убедиться в этом, используя id(func)
or func.__name__
внутри inner
, это то же самое, что и до decorate . И часть, которую я просто не понимаю. Когда рекурсивная функция выполняется в строке result = func(n)
внутри inner
, func(n)
вызывает оформленный fact
(или inner
) не оригинальный fact
. Это меня смущает, потому func
что все еще указывает на оригинал fact
.
Почему рекурсивная функция внутри декоратора вызывает свою внутреннюю функцию, а не исходную функцию, которая была передана декоратору и все еще имеет ссылку на нее?
Комментарии:
1.
inner
заменяетfact
. Любой рекурсивный вызовfact
является вызовомinner
.2. На самом деле она вызывает оба, но
fact
вызывается внутриinner
вызова.
Ответ №1:
Как уже объяснил @ inner()
khelwood, печатается 4 раза, потому что рекурсия вызывает inner
функцию 4 раза, легко.
Ваша fact
функция вызывается в этой строке result = func(n)
внутри inner
функции. Если у вас print(fact.__name__)
есть вывод inner
, потому что fact
это inner
функция.
Это может сбить с толку, потому что каждая функция, которую вы передаете в свой log
декоратор, станет inner
функцией. Чтобы избежать этой путаницы, я предлагаю использовать wraps
декоратор следующим образом:
from functools import wraps
def log(func):
@wraps(func)
def inner(n):
print('inner()')
result = func(n)
return result
return inner
Таким образом, func
вы передаете внутри своего декоратора сохранит исходную идентичность и print(fact.__name__)
будет печатать fact
.
Комментарии:
1. Если я это делаю
func.__name__
, илиid(func)
внутриinner
, он печатаетfact
и по-прежнему имеет ссылку на исходную функцию. Почему тогдаfunc(n)
вызываетinner
?2. Потому
fact
что это и имя вашей функции, и переменная. Вы не должны этого делать, потому что это сбивает с толку. Когда вы пишетеfact
, это ссылается на переменную, а не на функцию, и поскольку значение переменнойfact
являетсяlog(fact)
log
функцией, вызывается и возвращаетсяinner
. Поэтому измените имя вашей переменной на что-то вродеlogging_fact
, и это будет понятнее.3. @edzoons В основном то, что вы
log()
делаете, возвращаетinner
функцию, любая переменная, которой вы присваиваетеlog()
, будет внутренней функцией. Вы перезаписываетеfact
имя, а этого делать не следует.4. Ох. Наконец-то я понял, что вы оба (@JLeno46 и @khelwood ) пытались мне сказать. Исходная
fact
функция загружает глобальное имяfact
, которое, кстати, является оформленнымfact
. Спасибо.5. @khelwood если я изменю имя
fact
наlogging_fact
, функция больше не будет оформлена. Тем не менее, это помогло мне понять это.