lambda与tkinter奇怪地合作

Doc*_*lar 2 python lambda tkinter

我正在尝试使用Tkinter制作计算器.我一直试图清理代码,但遇到了麻烦.创建按钮时,我使用以下代码:

Button(self,text="1",command=lambda: self.addkey("1"),width=self.defaultwidth,height=self.defaultheight).grid(row=5, column=0)
Button(self,text="2",command=lambda: self.addkey("2"),width=self.defaultwidth,height=self.defaultheight).grid(row=5, column=1)
Button(self,text="3",command=lambda: self.addkey("3"),width=self.defaultwidth,height=self.defaultheight).grid(row=5, column=2)
Run Code Online (Sandbox Code Playgroud)

以下是命令调用

def addkey(self,key):
            # Adds a given key to the display
            if len(self.displaytext) + len(key) <= self.maxlength:
                self.displaytext += key
                self.display["text"] = self.displaytext
Run Code Online (Sandbox Code Playgroud)

按顺序按下按钮1,2和3时,输出如下:

123
Run Code Online (Sandbox Code Playgroud)

我一直在尝试清理代码,使它看起来更像:

for i in range(3):
    Button(self,text=str(i+1),command=lambda: self.addkey(str(i+1)),width=self.defaultwidth,height=self.defaultheight).grid(row=5, column=i)
Run Code Online (Sandbox Code Playgroud)

这样可以很好地添加按钮,但是当按顺序按下1,2和3时,屏幕上会显示以下内容:

333
Run Code Online (Sandbox Code Playgroud)

我想知道我是否错过了什么,或者这根本不可能.

sle*_*ica 5

啊,范围.当你这样做:

command=lambda: self.addkey(str(i))
Run Code Online (Sandbox Code Playgroud)

不是那里 "解决" i一个数字.你只是告诉拉姆达引用当它被调用,事后.i

for循环结束后的任何时间,i = 3(最后一个值),所以你的所有lambda都会3在他们要求的时候得到i.

如果我没有弄错的话,你可以添加一个函数作为间接手段,它将适当地"捕获" i周围的范围.

def add_key_f(i):
    return lambda self: self.addkey(i)
Run Code Online (Sandbox Code Playgroud)

  • 另一种选择:`command = functools.partial(self.addkey,str(i))`. (4认同)
  • 这个问题的经典解决方法(除了你提到的那个)是添加一个关键字arg:`command = lambda i = i:self.addkey(str(i))` (3认同)
  • @BryanOakley这有点脏,因为它是滥用关键字参数机制将值导入lambda(否则不会使用或需要`i`参数).但是,它非常简短,易于理解,它*是一个Python习语,所以它的使用不应被视为混淆. (2认同)