我在Python中学习了一点Tkinter,创建了交互式窗口。我正在制作的窗口是这样的窗口,给定联系人的字典及其各自的联系信息,为每个联系人创建一个按钮,当按下该按钮时,显示他们的联系信息。
我所拥有的两个样本联系人名为' Marvin'和' Minsky',他们的信息存储在名为book
的字典中:
import Tkinter as tkinter
# 'Phonebook' with contact info for two people.
book = {"Marvin": {"Mobile": "1234567890", "Email": "marvin@gmail.com"},
"Minsky": {"Mobile": "9087865342", "Email": "minsky@yahoo.com"}}
window = tkinter.Tk() # Make window object
def showinfo(name):
# Displays info for person whose button was clicked
# Displays info by configuring labels named 'mobile' and 'email',
# using values in 'book'
mobile.configure(text = book[name]["Mobile"])
email.configure(text = book[name]["Email"])
这是重要的部分。而不是简单地使用command = showinfo(name)
来调用showinfo()
按钮,而不是我第一次尝试使用lambda表达式:< / p>
for name in sorted(phonedict.book.keys()):
# Create button for each alphabetically sorted name
btn = tkinter.Button(text = name, command = lambda arg = name: showinfo(arg))
btn.pack()
其余代码只是showinfo()
修改的标签:
# Window section where contact info is displayed via labels
mobile_lbl = tkinter.Label(text = "Mobile:")
mobile_lbl.pack()
mobile = tkinter.Label(text = "")
mobile.pack()
email_lbl = tkinter.Label(text = "Email:")
email_lbl.pack()
email = tkinter.Label(text = "")
email.pack()
# Display window
window.mainloop()
运行时,此程序完全符合我的要求,在单击每个按钮时正确修改标签。
如果使用command = showinfo(name)
代替lambda,则抛出NameError: global name 'mobile' is not defined
,因为(我认为)它会在创建按钮时尝试执行按钮的命令 而不是按下。
为什么在按钮的命令中使用lambda表达式会使命令执行,直到单击按钮为止?服务的功能目的是什么?
答案 0 :(得分:3)
就像你猜的那样,当你使用List<ClientFileList>
时,你要求python立即调用command = showinfo(name)
,然后将结果分配给showinfo(name)
属性。
command
属性必须为函数指定引用。 command
是创建匿名函数并返回引用的便捷方式,该引用将分配给属性。在匿名函数中,你可以调用你想要的任何其他函数,并且在执行匿名函数之前不会执行内部代码。
lambda
的功能目的是创建一个临时的,未命名的函数,该函数可以传递给其他函数或作为属性存储。在一个需要参数的回调周围创建一个包装器是一种方便的方法(但不是唯一的方式 1 )。
1 完成同样事情的另一种方法是使用functools.partial。另一种方法是编写自己的decorator。
答案 1 :(得分:2)
回调只是一个传递给其他函数的函数,因此这些函数可以调用它。 showinfo(name)
不是回调函数,因为函数是在构造Button
之前立即调用的,并且showinfo
的返回值似乎是None
(如果函数没有&#39}。 t在Python中默认返回它返回None
的任何内容。
showinfo
本身,可能是一个回调,因为它是一个函数,但问题是它需要一个位置参数。在Python中,需要位置参数:
def f1(callback): callback()
def f2(arg1): pass
f1(f2) # TypeError: f2() takes exactly 1 argument (0 given)
您的代码解决此问题的方式是使用lambda表达式,该表达式将我们即时定义的默认参数作为名称。所以我们所说的是,这里有一个函数,通过一个关键字参数arg
,默认情况下设置为name
,当调用此函数时,使用该默认参数调用showinfo:
btn = tkinter.Button(text = name, command=lambda arg=name: showinfo(arg))
但为什么我们需要使用默认参数?难道不是一种复杂的做事方式吗? 是的,是的。你可以这样做:
btn = tkinter.Button(text = name, command=lambda: showinfo(name))
这是一个不带参数的lambda,与你正在做的完全有效。这是因为 showinfo
和name
是你正在创建的lambda闭包的一部分,所以即使在将lambda函数传递给另一个函数之后,lambda本身也可以访问它们。
是的,但这是必要的,因为您传入的每个lambda函数在创建lambda时都需要name 的值。依靠闭包来保持name
的值不会成功,因为name
的值会在循环的每次迭代中发生变化。