Python链接装饰器覆盖属性

时间:2016-11-04 17:02:00

标签: python python-2.7 python-decorators

我有两个装饰器。每个装饰器都有一个函数作为参数。每个装饰器都为该函数设置一个属性。在单个函数上链接装饰器后,我希望看到2个新属性。然而,顶部装饰器t2“覆盖”属性t1集。否则,在解决所有问题后t1不再存在。任何人都可以解释为什么以及如何解决它?

def t1(function):
 def wrapper(*args, **kwargs):
  setattr(wrapper, "t1", True)
  return function(*args, **kwargs)
 setattr(wrapper, "t1", False)
 return wrapper

def t2(function):
 def wrapper(*args, **kwargs):
  setattr(wrapper, "t2", True)
  return function(*args, **kwargs)
 setattr(wrapper, "t2", False)
 return wrapper

@t2
@t1
def test():
 pass

1 个答案:

答案 0 :(得分:3)

它发生了,因为你的装饰器在包装器上设置属性。当第一个装饰在其包装器上设置属性时,它将包装器传递给第二个装饰器,在第一个装饰器的顶部添加另一个包装器,并在第二个包装器上设置属性。所以你最终得到了第二个包装器。

In [3]: def decorator_a(fn):
   ...:     def wrapper(*args, **kwargs):
   ...:         return fn(*args, **kwargs)
   ...:     print("I'm setting the attribute on function {}".format(id(wrapper)))
   ...:     setattr(wrapper, "attr1", True)
   ...:     return wrapper
   ...: 

In [4]: def decorator_b(fn):
   ...:     def wrapper(*args, **kwargs):
   ...:         return fn(*args, **kwargs)
   ...:     print("I'm setting the attribute on function {}".format(id(wrapper)))
   ...:     setattr(wrapper, "attr2", True)
   ...:     return wrapper
   ...: 

In [5]: first_time_decorated = decorator_a(lambda x: x)
I'm setting the attribute on function 4361847536

In [6]: second_time_decorated = decorator_b(first_time_decorated)
I'm setting the attribute on function 4361441064

您可以通过设置包装器上装饰的函数的所有属性

来解决此问题
In [14]: def decorator_a(fn):
    ...:     def wrapper(*args, **kwargs):
    ...:         return fn(*args, **kwargs)
    ...:     setattr(wrapper, "attr1", True)
    ...:     for attribute in set(dir(fn)) - set(dir(wrapper)):
    ...:         setattr(wrapper, attribute, getattr(fn, attribute))
    ...:     return wrapper
    ...: 

In [15]: def decorator_b(fn):
    ...:     def wrapper(*args, **kwargs):
    ...:         return fn(*args, **kwargs)
    ...:     setattr(wrapper, "attr2", True)
    ...:     for attribute in set(dir(fn)) - set(dir(wrapper)):
    ...:         setattr(wrapper, attribute, getattr(fn, attribute))
    ...:     return wrapper
    ...: 

In [16]: first_time_decorated = decorator_a(lambda x: x)

In [17]: second_time_decorated = decorator_b(first_time_decorated)

In [18]: second_time_decorated.attr1
Out[18]: True

In [19]: second_time_decorated.attr2
Out[19]: True