2016-12-07 9 views
1

지역 변수를 플래시하지 않는 이유를 이해 : 나는 foo 여러 번 호출하는 경우장식 : 그것은 내가 간단한 장식을 작성했습니다

이제
from functools import wraps 
import random 

def my_dec(f): 
    lst = list() 

    @wraps(f) 
    def wrapper(*args): 
     lst.append(random.randint(0, 9)) 
     print(lst) 
     return f(*args) 

    return wrapper 

@my_dec 
def foo(): 
    print("foo called") 

, lst 플러시되지 않습니다. 대신 시간이 지남에 따라 생성됩니다.

foo() 
> [4] 
> foo called 

foo() 
> [4, 9] 
> foo called 

foo() 
> [4, 9, 1] 
> foo called 

... 

이유는 무엇입니까 : 따라서, foo의 여러 호출과 같은 출력을 반환? 난 decorator 그냥 my_dec(foo)에 대한 구문 설탕 생각?! 나는 각각 my_dec에 대한 호출이 lst을 플러시한다고 가정했다.

+2

당신은 장식자가'foo = my_dec (foo)'에 대한 문법적 설탕이라는 것이 맞습니다 ... 그러나 이것이 어떻게 당신이'lst'가 "플러시 (flushed)"될 것이라고 생각하게 할지를 보지 못합니다. 여기에주의하십시오 -'my_dec'는'foo'라고 몇 번이나 호출 되더라도 한 번만 호출됩니다 ... – mgilson

+0

아, 그래요. 'my_dec'는'foo'가 호출 될 때마다 호출됩니다 만,'foo = my_dec (foo)'는 한 번만 발생하기 때문에 분명히 아닙니다. 설명 주셔서 감사합니다! – daniel451

+0

추가 질문 : 어떻게해서든지 '처음'에 액세스 할 수 있습니까? – daniel451

답변

3

당신 말이 맞아요 ... 데코레이터는 단지 통사론적인 설탕입니다. 특히 :

def bar(): 
    pass 
foo = decorator(bar) 
del bar 
:

def foo(): 
    pass 
foo = decorator(foo) 

은이 또 다른 방법 대부분 해당 1을의 좀 더 엉뚱한하자 및 재 작성 :

@decorator 
def foo(): 
    pass 

정확히 같은 일이 같다

이 방법을 사용하여 잘 작성하면 if 나는 번 foo 번이라고 부르는데, 나는 이 아니며 번으로 전화하면 decorator 번이 번다. decorator은 한 번만 호출되었습니다 (foo 생성을 돕기 위해). 당신의 예에서 이제

, 당신의 데코레이터가 호출되는 경우 즉시 목록을 만듭니다 :

def my_dec(f): 
    lst = list() # list created here! 

    @wraps(f) 
    def wrapper(*args): 
     lst.append(random.randint(0, 9)) 
     print(lst) 
     return f(*args) 

    return wrapper 

이 기능은 foo를 호출 할 때 그래서, 당신은 wrapper를 호출하고, wrapper이 당신의 foo에 할당됩니다 돌아왔다. lst - lst에 더 많은 요소를 추가하는 코드 만 있으므로 lst을 나타내야 할 것이므로 여기에는이 호출간에 플러시되어야한다는 코드는 wrapper에 없습니다.

1

은 '


이 또한주의 (데코레이터가하는 일에 따라 ... 당신은 함수의 __name__ 속성에서 약간의 차이를 볼 수 있지만 그렇지 않으면 같은 일이) 데코레이터가 호출 될 때마다 하나씩 lst이 있습니다. 우리가 foobar를 호출 할 때 다음, 우리는거야

@my_dec 
def foo(): 
    pass 

@my_dec 
def bar(): 
    pass 

그리고 우리는 우리가 좋아하는 경우에이 일에 미쳐 두 번 foo을 장식 할 수 있습니다

@my_dec 
@my_dec 
def foo(): 
    pass 

또는 우리는 하나 개 이상의 기능을 장식 할 수 있습니다 그들 각각이 그들 자신의 (뚜렷한) 난수 목록을 축적한다는 것을 알아라.즉, 데코레이터가 무언가에 적용될 때마다 새로운 목록이 생성되고 "무언가"가 호출 될 때마다 목록이 커집니다.

+0

잘 넣어 - 좋은 설명! 추가 질문 : 꾸며진 모든 함수 'foo_n'은 자체 'lst'를 얻습니다. 맞습니까? 따라서, 아마도 이런 식으로 물건을 외울 수 있을까요? 'lst'에 접근 할 수있는 방법이 있습니까? – daniel451

+1

@ascenator - 나는 각각이 그것의 자신의'lst'를 얻는다 고 말하기 위해 편집을 업데이트하고 있었다. 목록에 액세스 할 수있는 _clean_ 방법이 없습니다. 당신은 그것을 반환하기 전에 함수에 속성 ​​('wrapper')을 추가 할 수 있습니다 ... 'wrapper.lst = lst; 반환 래퍼',하지만 그게 약간의 kludgy을 느낄 시작 – mgilson

+0

당신이 저장하고 값을 저장이 방법은 * pythonic * 아니에요? 또는 일반적으로 데코레이터를 사용하여 특정 데이터를 저장하고 나중에 읽을 수있는 명확한 방법이 없다는 것입니까? 이런 식으로 가치를 저장하는 것은 좋은 생각 인 것 같아서 방금 내 마음에 왔습니다. 그렇지 않으면'foo_n'이 호출 될 때마다 수동으로이 작업을 수행해야합니다. – daniel451