带有可变参数的回调函数 tkinter 按钮
Posted
技术标签:
【中文标题】带有可变参数的回调函数 tkinter 按钮【英文标题】:Callback function tkinter button with variable parameter 【发布时间】:2013-11-10 16:58:16 【问题描述】: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... 非常感谢你帮助我
【问题讨论】:
【参考方案1】:您的匿名 lambda
函数可以看作是闭包(正如@abernert 指出的那样,在 Python 的情况下,它们实际上不是闭包) - 它们“关闭”变量 @987654322 @,稍后参考。但是,它们不会在定义时查找值,而是在调用时,也就是在整个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))
...
【讨论】:
非常感谢! :) 这不太准确,因为这里 not 实际上是一个闭包。但它比我的解释更简洁,希望新手更容易理解。 @abarnert:不过,感谢您提及它 - 正是这样的小技术让我喜欢 ***,并在此过程中学习。【参考方案2】:Python 常见问题解答中对此进行了解释:Why do lambdas defined in a loop with different values all return the same result?。
引用常见问题解答:
发生这种情况是因为 x 不是 lambda 的本地变量,而是在外部范围内定义的,并且在调用 lambda 时访问它——而不是在定义它时...
为了避免这种情况,您需要将值保存在 lambda 的局部变量中,这样它们就不会依赖全局的值……
换句话说,您的新函数不存储 i
的值,而是存储变量 i
。他们都在存储 same 变量i
,在循环结束时它的值是10
。事实上,如果您在 F.mainloop()
之前添加一个 i = 'spam'
,您会看到所有按钮现在都打印出字符串 spam
而不是数字。
这在您尝试创建闭包时非常有用 - 可以影响其定义环境的函数。* 但是当您不尝试这样做时,这可能会妨碍您。
解决此问题的最简单方法是使用具有默认值的参数。默认值不包含变量;只是在定义函数时评估的值。所以:
newButton = Button(F,text="Show Number", command=lambda num=i: showNumber(num))
* 请注意,在这种情况下,实际上不涉及任何闭包,因为i
是全局的,而不是封闭范围内的局部。但实际上,这只是因为 Python 对全局变量有特殊处理,这里不需要闭包;从概念上讲,如果您认为有一个,除非您开始查看 __closure__
或 __code__
属性,否则您不会遇到任何麻烦。
【讨论】:
好点,我不知道 Python 处理模块级变量的方式不同。以上是关于带有可变参数的回调函数 tkinter 按钮的主要内容,如果未能解决你的问题,请参考以下文章