2016-11-04 5 views
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'는'test'가 아닌 * 데코 레이팅 된 * 함수를 매개 변수로 기대해야합니다. –

+1

문체적인 측면에서 [PEP8 표준] (https://www.python.org/dev/peps/pep-0008/#indentation)은 들여 쓰기를 위해 4 칸을 요구합니다. 문법적으로 1 개만 작동하지만 읽는 것은 정말 어렵고 다른 사람들과 코드를 공유하기가 어렵습니다. –

+0

예, 그렇습니다. t2의 입력을 처리 (함수)하면 속성 t1이 표시됩니다. 그러나, 만약 내가 dir (test)하면, 나는 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

관련 문제