2013-07-16 47 views
4

我正在使用lambda函數進行tkinter的GUI編程。 實現按鈕,當最近我被困打開文件:Python:使用和不使用關鍵字參數的lambda函數行爲?

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)) 

lambda函數有一個關鍵字參數傳遞的文件路徑。在這種情況下,當self.file爲時,參數f未更新。

我從代碼片段中獲得了關鍵字參數,並且在任何地方都使用它。顯然我不應該...

這仍然不清楚我可以有人解釋我的兩個lambda表單之間的區別,以及何時使用另一個?

謝謝!

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 

與此相比,功能b()看起來像

>>> 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()

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

 >> 13 FOR_ITER    11 (to 27) 
      16 STORE_FAST    0 (i) 

:在STORE_DEREF存儲在cell對象(關閉),而STORE_FAST採用的是 「正常」 的變量,它(可能)的工作原理有點快。

拉姆達也有差別:

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

這裏有一個LOAD_GLOBAL,而上面的一個採用LOAD_DEREF。後者也是關閉的。

我完全忘了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就好像它會取值: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'作爲參數,你需要lambda中的參數。之後,每次我需要lambda函數時,我都使用kwd參數。這是不正確的... – Plouff