2013-07-16 5 views
4

나는 tkinter를 이용한 GUI 프로그래밍을 위해 람다 함수를 사용하고있다. 버튼을 구현할 때 최근에 나는이 붙어있어 그 열려있는 파일 : 보시다시피파이썬 : 키워드 인수가 있거나없는 람다 함수 동작?

self.file="" 
button = Button(conf_f, text="Tools opt.", 
     command=lambda: tktb.helpers.openfile(self.file)) 

, 나는 업데이트 할 수있는 파일의 경로를 정의 할, 그리고 GUI를 만들 때 그 알 수 없습니다. 내가 가진 문제는 이전에 내 코드라고이다 :

button = Button(conf_f, text="Tools opt.", 
     command=lambda f=self.file: tktb.helpers.openfile(f)) 

람다 함수는 파일 경로를 전달하는 키워드 인수했다. 이 경우 self.file 일 때 매개 변수 f이 업데이트되지 않았습니다.

나는 코드 스 니펫에서 키워드 인수를 얻었으므로 어디에서나 사용할 수 있습니다. 분명히 나는 ​​안된다. ...

이것은 나에게 아직도 명확하지 않다. 누군가 나에게 두 개의 람다 형식의 차이점과 다른 것을 사용할 때를 설명 할 수 있을까?

감사합니다.

PS : 다음 코멘트는 솔루션에 저를 주도하지만 난 좀 더 설명을하고 싶습니다 : lambda working oddly with tkinter

+0

더 많은 설명이 필요합니까? 링크 된 질문에 대한 대답을 통해 차이점을 매우 간결하게 설명합니다. – BrenBarn

+0

사실, 인수 값을 코딩 스타일에 따라 업데이트 할 수있는 이유는 무엇인지 이해하고 싶습니다. – Plouff

답변

7

내가 깊이에 더 그것을 설명하려고합니다.

당신이

i = 0 
f = lambda: i 

당신이 (람다은 본질적으로 기능입니다) 포위 스코프의 i 변수에 액세스하는 함수를 만드는 작업을 수행합니다.

내부적으로는 i을 포함하는 소위 폐쇄가 있습니다. 느슨하게 말해서, 서로 다른 시점에서 다른 값을 가질 수있는 실제 변수에 대한 포인터의 일종입니다.

def a(): 
    # first, yield a function to access i 
    yield lambda: i 
    # now, set i to different values successively 
    for i in range(100): yield 

g = a() # create generator 
f = next(g) # get the function 
f() # -> error as i is not set yet 
next(g) 
f() # -> 0 
next(g) 
f() # -> 1 
# and so on 
f.func_closure # -> an object stemming from the local scope of a() 
f.func_closure[0].cell_contents # -> the current value of this variable 

여기서, i의 모든 값은 - 그 시간에 - 상기 클로저에 저장된다. 기능이 f() 인 경우 필요합니다. 그것은 거기에서 그들을 얻는다.

당신은 분해 명부에 그 차이를 볼 수 있습니다

이 말했다 기능 a()f()을 다음과 같이 분해 :

>>> dis.dis(a) 
    2   0 LOAD_CLOSURE    0 (i) 
       3 BUILD_TUPLE    1 
       6 LOAD_CONST    1 (<code object <lambda> at 0xb72ea650, file "<stdin>", line 2>) 
       9 MAKE_CLOSURE    0 
      12 YIELD_VALUE 
      13 POP_TOP 

    3   14 SETUP_LOOP    25 (to 42) 
      17 LOAD_GLOBAL    0 (range) 
      20 LOAD_CONST    2 (100) 
      23 CALL_FUNCTION   1 
      26 GET_ITER 
     >> 27 FOR_ITER    11 (to 41) 
      30 STORE_DEREF    0 (i) 
      33 LOAD_CONST    0 (None) 
      36 YIELD_VALUE 
      37 POP_TOP 
      38 JUMP_ABSOLUTE   27 
     >> 41 POP_BLOCK 
     >> 42 LOAD_CONST    0 (None) 
      45 RETURN_VALUE 
>>> dis.dis(f) 
    2   0 LOAD_DEREF    0 (i) 
       3 RETURN_VALUE 

비교하는

>>> def b(): 
... for i in range(100): yield 
>>> dis.dis(b) 
    2   0 SETUP_LOOP    25 (to 28) 
       3 LOAD_GLOBAL    0 (range) 
       6 LOAD_CONST    1 (100) 
       9 CALL_FUNCTION   1 
      12 GET_ITER 
     >> 13 FOR_ITER    11 (to 27) 
      16 STORE_FAST    0 (i) 
      19 LOAD_CONST    0 (None) 
      22 YIELD_VALUE 
      23 POP_TOP 
      24 JUMP_ABSOLUTE   13 
     >> 27 POP_BLOCK 
     >> 28 LOAD_CONST    0 (None) 
      31 RETURN_VALUE 
과 같은 기능 b()

루프의 주요 차이점은

입니다.
 >> 13 FOR_ITER    11 (to 27) 
      16 STORE_FAST    0 (i) 

a()에서

 >> 27 FOR_ITER    11 (to 41) 
      30 STORE_DEREF    0 (i) 

b()에서하십시오 cell 객체 (폐쇄)에서 STORE_DEREF 상점, STORE_FAST 동안 (아마) 조금 더 빠르게 작동하는 "정상"변수를 사용합니다.하나는 위의 LOAD_DEREF를 사용하면서 여기

>>> dis.dis(lambda: i) 
    1   0 LOAD_GLOBAL    0 (i) 
       3 RETURN_VALUE 

당신이하는 LOAD_GLOBAL 있습니다

뿐만 아니라 람다는 차이가 있습니다. 후자 역시 폐쇄를위한 것이다.

완전히 lambda i=i: i을 잊었습니다. 당신은 기본 매개 변수 값이있는 경우

, 그것은 완전히 다른 경로를 통해 기능에 영향을 찾습니다

>>> i = 42 
>>> f = lambda i=i: i 
>>> dis.dis(f) 
    1   0 LOAD_FAST    0 (i) 
       3 RETURN_VALUE 
: i의 현재 값은 기본 매개 변수를 통해 방금 만든 함수에 전달됩니다

이렇게하면 함수는 f()으로 호출됩니다. 누락 된 인수가 있음을 감지하고 해당 매개 변수를 기본값으로 채 웁니다. 이 모든 것은 함수가 호출되기 전에 발생합니다. 함수 내에서 값을 가져 와서 반환한다는 것을 알 수 있습니다.

그리고 또 다른 방법으로 작업을 수행 할 수 있습니다. 람다를 값으로 사용하는 것처럼 그냥 사용하십시오 : lambda i: i. 이것을 호출하면 누락 된 인수에 대해 불평합니다.

하지만 functools.partial의 사용과 그 대처 할 수 있습니다

ff = [functools.partial(lambda i: i, x) for x in range(100)] 
ff[12]() 
ff[54]() 

이 래퍼는 호출과 인수의 수를 가져옵니다이 전달 될 수 있습니다. 결과 오브젝트는이 인수와 사용자가 제공 한 모든 인수를 사용하여 원래 호출 가능 함수를 호출하는 호출 가능 함수입니다. 의도 된 값으로 잠긴 상태로 유지하기 위해 여기에서 사용할 수 있습니다.

+0

그래서 차이를보고 싶다면 1 /'dis.dis (lambda : i)'와 2 /'dis.dis (lambda i = i : i)'를 실행해야합니다. 나는 하나의'LOAD_GLOBAL'과 하나의'LOAD_DEREF'를 얻을 것이다. 나는 지금 이것을 할 수는 없지만 나는 내일 시험 할 것이다. 대답 해 주셔서 감사합니다! 또한 함수 클로저에 대한 추가 정보가 필요합니다. 나는 그것에 대해 아무것도 듣지 못했습니다 ... – Plouff

+0

@Plouff 대략 말했습니다. 나는 그것에 대해 좀 더 생각해 보았다. – glglgl

+0

답변 해 주셔서 감사합니다. 나는 이런 종류의 일에 익숙하지 않았기 때문에 모든 것이 분명하다고 말할 수는 없다. 그러나 가장 중요한 부분을 이해했다고 생각합니다. 나는 또한 마음이 엉망이되는 것이 tkinter 바인딩이라는 것을 알아 차렸다. 'event'를 인수로 전달해야하므로 람다에 인수가 필요합니다. 그 후에 람다 함수가 필요할 때마다 kwd 인수를 사용했습니다. 그리고 그것은 정확하지 않았습니다 ... – Plouff