如何解决未从 for 循环中正确调用 Python lambda 函数
我正在尝试使用 Python 中的 Tkinter 制作一个计算器。我使用 for 循环来绘制按钮,并且我正在尝试使用 lambda 函数,因此按钮的操作仅在按下时调用,而不是在程序启动时调用。但是,当我尝试这样做时,会抛出“列表索引超出范围”错误。
这是绘制按钮并设置其进程的相关部分:
# button layout
# ( ) AC **
# 7 8 9 /
# 4 5 6 *
# 1 2 3 -
# 0 . = +
# list that holds the buttons' file names in order
imgOrder = ["lpr","rpr","clr","pow","7","8","9","div","4","5","6","mul","1","2","3","sub","0","dot","eql","add"];
# list containing the actual values assigned to buttons in order
charOrder = ["(",")","AC","**","/","*","-",".","=","+"];
imgIndex = 0;
for rowi in range(1,6):
for coli in range(4):
# fetch and store the img
img = PhotoImage(file="images/{}.gif".format(imgOrder[imgIndex]));
# create button
button = Button(self,bg="white",image=img,borderwidth=0,highlightthickness=0,activebackground="white",command=lambda: self.process(charOrder[imgIndex]));
# set button image
button.image = img;
# put the button in its proper row and column
button.grid(row=rowi,column=coli,sticky=N+S+E+W);
imgIndex += 1;
# pack the GUI
self.pack(fill=BOTH,expand=1);
# processes button presses
def process(self,button):
print("Button {button} pressed!");
# AC clears the display
if (button == "AC"):
# clear
self.display["text"] = "";
# otherwise,the number/operator can be tacked on the end
else:
self.display["text"] += button;
特别是抛出错误的那一行是带有 lambda 函数的那一行,但我不知道有什么方法可以只在点击时注册按钮,而不是单独为每个按钮写出这个块。>
解决方法
这是一个典型的不需要关闭的案例。这是一个简化的示例:
funcs = []
for i in range(3):
funcs.append(lambda: i + 1)
for func in funcs:
print(func())
人们可能期望这会打印 1 2 3
,但它会打印 3 3 3
。原因是,lambda
是一个 闭包,关闭变量 i
(在其上下文中捕获它)。当我们执行函数时,变量 i
保留在循环中的最后一个值 (2
)。重申一下,lambda
不捕获值,而是变量。为了避免这种情况,我们希望将 i
的当前值作为参数传递到函数中。为此,我们可以构造一个接受 i
作为参数的函数,将 参数 捕获到闭包中并返回我们想要的“自定义”函数:
from functools import partial
funcs = []
for i in range(3):
funcs.append((lambda val: lambda: val + 1)(i))
for func in funcs:
print(func())
等效地,我们可以使用 functools.partial
,它就是这样做的:
from functools import partial
funcs = []
for i in range(3):
funcs.append(partial(lambda val: val + 1,i))
for func in funcs:
print(func())
这里,lambda val: val + 1
需要一个参数; partial(lambda val: val + 1,0)
将产生一个新函数,其中第一个参数固定在 0
- 基本上是 lambda: 0 + 1
。这不会捕获任何变量,从而避免了您遇到的问题。
tl;博士:
command=partial(lambda i: self.process(charOrder[i]),imgIndex)
,
问题是双循环末尾的变量 imgIndex
是 20。lambda 表达式将 self.process(charOrder[imgIndex])
绑定到 变量,no 绑定到它的值,所以在循环退出时,它将评估为 self.process(charOrder[20])
。
一种解决方法是使用 lambda imgIndex=imgIndex: self.process(charOrder[imgIndex])
,它将按值传递参数。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。