2016-11-04 81 views
2

我有兩個裝飾器。每個裝飾器都有一個作爲參數的函數。每個裝飾器爲該功能設置一個屬性。在單個函數上鍊接裝飾器之後,我希望看到2個新屬性。但是,頂級裝飾器t2會「覆蓋」屬性t1集合。否則,在解決所有問題後,t1不再存在。任何人都可以解釋爲什麼以及如何解決它?Python鏈式裝飾器覆蓋屬性

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

't1'「包裝」功能'test',然後't2'「包裝」的函數由't1'返回。因此't2'應該期望一個*修飾*函數作爲它的參數 - 而不是'測試'。 –

+1

作爲一種文體學觀點,[PEP8標準](https://www.python.org/dev/peps/pep-0008/#indentation)要求4個空格用於縮進。雖然只有1個在語法上起作用,但它很難閱讀,並且很難與其他人分享您的代碼。 –

+0

是的,我明白了。如果我dir(函數)t2的輸入,我看到屬性t1。但是,如果我dir(測試),我只看到t2(t1被刪除)。 – Sonny

回答

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 
+0

或者更好的是,使用'functools.wraps'來保存裝飾函數的元數據。 – thorhunter

+0

@thorhunter是的,儘管據我所知,'wraps'並不複製所有屬性。 –

+0

啊,好的,所以鏈接裝飾(包裝)產生包裹的包裝,這就是爲什麼我看不到t1。這現在更有意義了。謝謝。 – Sonny