带有可变参数的回调函数 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 按钮的主要内容,如果未能解决你的问题,请参考以下文章

C语言中如何实现可变参函数

可变参函数

Go语言 可变参数(变参函数)

函数 小结

函数参数的应用

可变长参数函数对象嵌套名称空间和作用域