2013-10-30 28 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