sup*_*o40 5 python tkinter function callback
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()
Run Code Online (Sandbox Code Playgroud)
所有按钮都返回10.为什么?
我想按钮1返回1,按钮2返回2 ...
非常感谢你帮助我
你的匿名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))
...
Run Code Online (Sandbox Code Playgroud)
这在Python FAQ中解释:为什么在具有不同值的循环中定义的lambdas都返回相同的结果?.
引用FAQ答案:
发生这种情况是因为x不是lambdas的本地,而是在外部作用域中定义,并且在调用lambda时访问它 - 而不是在定义时...
为了避免这种情况,你需要将值保存在lambda本地的变量中,这样它们就不依赖于全局的值......
换句话说,你的新函数不存储它们的值i,它们存储变量i.它们都存储了相同的变量i,它10在循环结束时具有值.事实上,如果您i = 'spam'之前添加了一个权利F.mainloop(),您将看到所有按钮现在打印出字符串spam而不是数字.
当您尝试创建可能影响其定义环境的闭包函数时,这非常有用.*但是当您不尝试这样做时,这可能会妨碍您.
最简单的方法是使用带有默认值的参数.默认值不包含变量; 只是在定义函数时计算的值.所以:
newButton = Button(F,text="Show Number", command=lambda num=i: showNumber(num))
Run Code Online (Sandbox Code Playgroud)
*请注意,在这种情况下,实际上并没有涉及任何闭包,因为它i是一个全局的,而不是封闭范围内的本地.但实际上,这只是因为Python对全局变量有特殊处理,而且不需要闭包; 从概念上讲,如果你想到有一个,除非你开始查看__closure__或__code__属性,否则你不会遇到任何麻烦.