Почему версия count_calls_bad
не сохраняет добавленный атрибут функции (декоратор добавляет .calls
к переданному в func) после его возврата? Я понимаю, что вторая (хорошая) версия является обязательной внутри внутренней функции по сравнению с плохой версией, которая пытается создать закрытие, где привязан атрибут func, но я думал, что «плохая» версия будет поддерживать ссылку на закрытую переменную, позволяя мне получить тот же результат, что и «хорошая» версия.Декоратор для отслеживания вызовов функций функций - не работает
def count_calls_bad(func):
func.calls = 0
def inner(*args,**kwargs):
func.calls += 1 #each call to inner increments func.calls (recur_n.calls)
return func(*args,**kwargs)
return inner
def count_calls_good(func):
def inner(*args, **kwargs):
inner.calls += 1
return func(*args, **kwargs)
inner.calls = 0
return inner
@count_calls_bad
def recur_n(num):
if num == 0:
return 0
print (num)
return recur_n(num-1)
recur_n(10)
print(recur_n.calls) #recur_n.calls attribute not bound any longer
UPDATE: фиксированный код, забыл обновить имя функции после тестирования в редакторе. Теперь recur_n вызывается, а не recur_10.
Кроме того, я играл вокруг и думаю, что этот вопрос recur_n становится inner
, а потом, что последняя строка print(recur_n.calls)
действительно print(function count_calls_bad.<locals>.inner at 0x000000000364E2F0>)
, и этот объект не имеет атрибута calls
, так как вызовы были связан с фактическим неукрашенным recur_n
,
Вы можете фактически заставить свой путь в оригинальную недекорированную функцию и получить правильно обновленный атрибут со следующей повозкой, запряженными волами:
print(recur_n.__closure__[0].cell_contents.calls)
Моя следующая мыслью была затем использовать functools @wraps сохранить оригинальную неукрашенный имя функции, так как это в основном то, что я делаю выше, попадая в декоратор и вытаскивая атрибут call
неадресного имени.
from functools import wraps
def count_calls_bad(func):
func.calls = 0
@wraps func
def inner(*args,**kwargs):
func.calls += 1 #each call to inner increments func.calls (recur_n.calls)
return func(*args,**kwargs)
return inner
Это, по крайней мере, дает мне результат, но этот результат равен нулю. Итак, теперь я ответил на свой собственный оригинальный вопрос, но в итоге получился новый. Почему, учитывая, что @wraps обновил функцию, так что recur_n теперь ссылается на recur_n, а не на внутреннюю, я получаю 0, а не 11?
Похоже, что @wraps копирует подпись функции, но не поддерживает ссылку или копирует другие данные, такие как переменные или атрибуты?