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

4

2 回答 2

8

您的匿名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))
    ...
于 2013-10-30T21:14:12.757 回答
5

这在 Python FAQ 中有解释:为什么在循环中定义的具有不同值的 lambdas 都返回相同的结果?.


引用常见问题解答:

发生这种情况是因为 x 不是 lambdas 的本地变量,而是在外部范围中定义的,并且在调用 lambda 时访问它——而不是在定义它时...</p>

为了避免这种情况,您需要将值保存在 lambda 的本地变量中,这样它们就不会依赖全局的值……</p>

换句话说,您的新函数不是存储 的值,i而是存储变量i。而且它们都存储相同的变量i,该变量10在循环结束时具有值。事实上,如果你在i = 'spam'前面添加一个 right F.mainloop(),你会看到所有的按钮现在都打印出字符串spam而不是数字。

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

解决此问题的最简单方法是使用具有默认值的参数。默认值不包含变量;只是在定义函数时评估的值。所以:

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

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

于 2013-10-30T21:15:47.783 回答