具有可变参数的回调函数tkinter按钮

时间:2013-10-30 21:04:52

标签: python function callback tkinter

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 ...
非常感谢你帮助我

2 个答案:

答案 0 :(得分:6)

你的匿名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))
    ...

答案 1 :(得分:5)

Python常见问题解答中解释了这一点:Why do lambdas defined in a loop with different values all return the same result?


引用常见问题解答答案:

  

这是因为x不是lambdas的本地,而是在外部作用域中定义,并且在调用lambda时访问它 - 而不是在定义它时...

     

为了避免这种情况,你需要将值保存在lambdas的本地变量中,这样它们就不依赖于全局值......

换句话说,您的新功能不会存储{em>值i ,而是存储变量i 。它们都存储相同的变量i,它在循环结束时具有值10。实际上,如果您在i = 'spam'之前添加F.mainloop(),您会看到现在所有按钮都打印出字符串spam而不是数字。

当您尝试创建可能影响其定义环境的闭包函数时,这非常有用。*但是当您尝试这样做时,这可能会妨碍。

最简单的方法是使用带有默认值的参数。默认值不包含变量;只是在定义函数时计算的值。所以:

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

*请注意,在这种情况下,实际上并没有涉及任何闭包,因为i是全局的,而不是封闭范围中的本地。但实际上,这只是因为Python对全局变量有特殊处理,并且不需要闭包;从概念上讲,如果您认为存在问题,除非您开始查看__closure____code__属性,否则不会遇到任何麻烦。