2015-01-14 3 views
3

Это продолжение моего предыдущего вопроса Change an attribute of a function inside its own body.Обтекание функции скрывает ее атрибуты?

Если я обернут функция так, что она сохраняет подсчет времени она была вызвана с помощью следующего декоратора:

def keep_count(f): 
    @wraps(f) 
    def wrapped_f(*args, **kwargs): 
     f(*args, **kwargs) 
     wrapped_f.count += 1 
    wrapped_f.count = 0 
    return wrapped_f 

А потом я хочу, чтобы обернуть его снова с чем-то другим:

def decorator2(fn): 
    @wraps(fn) 
    def fn_wrapper(*args, **kwargs): 
     if my_condition(fn): 
      fn(*args, **kwargs) 
    return fn_wrapper 

test_f = decorator2(test_f) 

Я больше не могу получить доступ к атрибуту функции count, как я надеюсь. Текущее значение атрибута count копируется через @wraps(fn), но если я снова вызову функцию, счет будет увеличиваться внутри исходной функции, но новое значение не будет скопировано в новую украшенную функцию.

>>> test_f() 
() {} 
>>> test_f.count 
1 
>>> test_f = decorator2(test_f) 
>>> test_f.count # The 1 gets copied here 
1 
>>> test_f() # Only the inner function's count increments... 
() {} 
>>> test_f.count # Still one, tho it should be two 
1 

Есть ли решение для этого? Как «постоянно» обертывание, или что-то лучше?

+0

@MartijnPieters Обновлено ... И да, я знаю об этом, я в основном прошу поделиться им: P –

ответ

4

functools.wraps() только копии по атрибуты. Когда вы затем увеличиваете счетчик на завернутой функции, вы назначаете целочисленное значение нового атрибуту, и оболочка все равно будет ссылаться на старое значение.

Вместо того, чтобы wraps копии через атрибуты, иметь его копию по всему __dict__ атрибуту функции:

from functools import wraps, WRAPPER_ASSIGNMENTS 

def decorator2(fn): 
    @wraps(fn, assigned=WRAPPER_ASSIGNMENTS + ('__dict__',), updated=()) 
    def fn_wrapper(*args, **kwargs): 
     if my_condition(fn): 
      fn(*args, **kwargs) 
    return fn_wrapper 

Теперь обернутые функции fn и fn_wrapper объектов имеют изменяемые __dict__ словаря имен и любые изменения к этому словарю видны в обеих функциях.

assigned является последовательностью атрибутов для копирования через (она обычно копируют вещи, как строка документации, имя функции и имя модуля), и updated является последовательностью атрибутов, которые должны рассматриваться как словари, где эти словари обновляются от завернутой функции. Последнее обычно установлено в __dict__, но теперь, когда мы копируем весь объект, нам больше не нужно обновлять его из оригинала.

+0

Ahh выглядит как решение, на которое я надеялся :) Что делает 'updated =()' делать? –

+0

@MarkusMeskanen: обновил ответ, чтобы объяснить, что означают два аргумента. –

+0

Спасибо, приму :) –

Смежные вопросы