为什么在此示例中需要 lambda 表达式? (Python)
Why is lambda expression necessary in this example? (Python)
我正在 Python 学习一点 Tkinter,创建交互式 windows。 window 我目前正在做的是,给定一个联系人字典及其各自的联系信息,为每个联系人创建一个按钮,按下该按钮时会显示他们的联系信息。
我的两个示例联系人名为 '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"])
这是重要的部分。 我第一次尝试使用 lambda 表达式,而不是简单地使用 command = showinfo(name)
调用 showinfo()
的按钮:
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 表达式会阻止命令在单击按钮之前执行?它有什么功能用途?
就像您猜到的那样,当您使用 command = showinfo(name)
时,您要求 python 立即 调用 ,然后将结果分配给command
属性。
必须为 command
属性提供对函数的 引用。 lambda
是创建匿名函数和 return 引用的便捷方法,引用被分配给属性。在该匿名函数内,您可以调用您想要的任何其他函数,并且在执行匿名函数之前不会执行内部代码。
lambda
的功能目的是创建一个临时的、未命名的函数,该函数可以传递给其他函数或存储为属性。这是一种方便的方式(但不是唯一的方式1)来围绕需要参数的回调创建包装器。
1另一种完成同样事情的方法是使用 functools.partial. Another method would be to write your own decorator。
回调只是您传递给其他函数的函数,以便这些函数可以调用它。 showinfo(name)
不是回调,因为在构造 Button
之前立即调用该函数,并且 showinfo
的 return 值似乎是 None
(如果一个函数在 Python 中 return 默认 None
没有 return 任何东西)。
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 函数都需要 name 在创建 lambda 时的值。依靠闭包来保持 name
的值是行不通的,因为 name
的值在循环的每次迭代中都会发生变化。
我正在 Python 学习一点 Tkinter,创建交互式 windows。 window 我目前正在做的是,给定一个联系人字典及其各自的联系信息,为每个联系人创建一个按钮,按下该按钮时会显示他们的联系信息。
我的两个示例联系人名为 '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"])
这是重要的部分。 我第一次尝试使用 lambda 表达式,而不是简单地使用 command = showinfo(name)
调用 showinfo()
的按钮:
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 表达式会阻止命令在单击按钮之前执行?它有什么功能用途?
就像您猜到的那样,当您使用 command = showinfo(name)
时,您要求 python 立即 调用 ,然后将结果分配给command
属性。
必须为 command
属性提供对函数的 引用。 lambda
是创建匿名函数和 return 引用的便捷方法,引用被分配给属性。在该匿名函数内,您可以调用您想要的任何其他函数,并且在执行匿名函数之前不会执行内部代码。
lambda
的功能目的是创建一个临时的、未命名的函数,该函数可以传递给其他函数或存储为属性。这是一种方便的方式(但不是唯一的方式1)来围绕需要参数的回调创建包装器。
1另一种完成同样事情的方法是使用 functools.partial. Another method would be to write your own decorator。
回调只是您传递给其他函数的函数,以便这些函数可以调用它。 showinfo(name)
不是回调,因为在构造 Button
之前立即调用该函数,并且 showinfo
的 return 值似乎是 None
(如果一个函数在 Python 中 return 默认 None
没有 return 任何东西)。
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 函数都需要 name 在创建 lambda 时的值。依靠闭包来保持 name
的值是行不通的,因为 name
的值在循环的每次迭代中都会发生变化。