为什么在这个例子中需要lambda表达式? (蟒蛇)

时间:2015-04-22 17:23:03

标签: python lambda tkinter tk

我在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表达式会使命令执行,直到单击按钮为止?服务的功能目的是什么?

2 个答案:

答案 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,与你正在做的完全有效。这是因为showinfoname是你正在创建的lambda闭包的一部分,所以即使在将lambda函数传递给另一个函数之后,lambda本身也可以访问它们。

是的,但这是必要的,因为您传入的每个lambda函数在创建lambda时都需要name 的值。依靠闭包来保持name的值不会成功,因为name的值会在循环的每次迭代中发生变化。