2013-10-30 70 views
3
from tkinter import * 

F=Tk() 

i=1 
while i<10: 
    newButton = Button(F,text="Show Number",command=lambda:showNumber(i)) 
    newButton.pack(side=TOP) 
    i+=1 

def showNumber(nb): 
    print(nb) 

F.mainloop() 

所有按鈕都會返回10.爲什麼?
我想按鈕1返回1,按鈕2回2 ...
非常感謝你對我的幫助具有可變參數的回調函數tkinter按鈕

回答

5

您的匿名lambda功能是可以的,雖然是封閉(如@abernert指出,他們是而不是實際上是在Python的情況下關閉) - 他們「關閉」變量i,稍後參考它。然而,他們在定義時沒有查找數值,而是在呼叫時這是之後整個while循環結束(此時,i等於10)時的, 。

要解決這個問題,您需要將i的值重新綁定到lambda使用的其他值。你可以在許多方面做到這一點 - 這裏有一個:

... 
i = 1 
while i < 10: 
    # Give a parameter to the lambda, defaulting to i (function default 
    # arguments are bound at time of declaration) 
    newButton = Button(F, text="Show Number", 
     command=lambda num=i: showNumber(num)) 
    ... 
+0

非常感謝! :) – superyo40

+0

這不太準確,因爲這裏實際上並不是封閉的。但這是一個比我的簡潔的解釋,希望更容易讓新手理解。 – abarnert

+0

@abarnert:謝謝你提到它 - 這是一點技術性的東西,讓我喜歡StackOverflow,並在這個過程中學習。 – voithos

5

這在Python FAQ解釋:Why do lambdas defined in a loop with different values all return the same result?


引述常見問題的答案:

這是因爲x是不是本地的lambda表達式,但在外部範圍的定義,它是訪問時的拉姆達被調用 - 不是當它定義...

爲了避免這種情況,您需要保存在局部變量的lambda表達式的值,使它們不依賴於全球的價值...

換句話說,您的新功能不存儲的值i,他們正在存儲變量i。並且它們全部存儲相同的變量i,在循環結束時它的值爲10。實際上,如果您在F.mainloop()之前添加i = 'spam',您會看到現在所有按鈕都打印出字符串spam而不是數字。

當您嘗試創建可能影響其定義環境的閉包功能時,這非常有用。但是當您嘗試這樣做時,而不是可能會阻礙。

最簡單的方法是使用帶默認值的參數。默認值不包含變量;只是在函數定義時評估的值。所以:

newButton = Button(F,text="Show Number", command=lambda num=i: showNumber(num)) 

*請注意,在這種情況下,有沒有實際參與任何關閉,因爲i是一個全球性的,而不是在封閉範圍內的局部。但實際上,這僅僅是因爲Python對全局變量有特殊的處理,並且在這裏不需要關閉;從概念上講,如果你想到有一個,你不會陷入任何麻煩,除非你開始看__closure____code__屬性。

+0

謝謝! :) 是工作 ! – superyo40

+0

好處,我不知道Python處理模塊級變量的方式不同。 – voithos