为啥使用 lambda 函数?
Posted
技术标签:
【中文标题】为啥使用 lambda 函数?【英文标题】:Why use lambda functions?为什么使用 lambda 函数? 【发布时间】:2011-03-16 14:49:10 【问题描述】:我可以找到很多东西来告诉我什么是 lambda 函数,以及语法如何工作以及什么不是。但除了“酷因素”(我可以在中间调用另一个函数,整洁!)之外,我还没有看到有什么东西可以非常有说服力地说明我为什么真的需要/想要使用它们。
在我见过的大多数示例中,它似乎更像是一种风格或结构选择。并且有点打破了python规则中的“只有一种正确的方法来做某事”。它如何使我的程序更正确、更可靠、更快或更容易理解? (我见过的大多数编码标准都倾向于告诉您避免在一行中使用过于复杂的语句。如果这样更容易阅读,请将其分解。)
【问题讨论】:
它不只是与命令式编程语言中的接口或函数/方法签名相对应的函数式编程吗..? (-> 用于指定回调,从而允许创建松散耦合的应用程序/软件组件。) Lambda 是“函数文字”。它们有用且方便的原因与列表和映射文字有用的原因相同(而不是手动执行append
等)。如果您在某些情况下允许其中一种,那么在所有情况下都没有理由反对另一种。
可能重复:Python Lambda - why?
这能回答你的问题吗? Why are Python lambdas useful?
【参考方案1】:
这是一个很好的例子:
def key(x):
return x[1]
a = [(1, 2), (3, 1), (5, 10), (11, -3)]
a.sort(key=key)
对
a = [(1, 2), (3, 1), (5, 10), (11, -3)]
a.sort(key=lambda x: x[1])
从另一个角度来看:Lambda 表达式也被称为“匿名函数”,在某些编程范式中非常有用,尤其是函数式编程,而 lambda 演算为其提供了灵感。
http://en.wikipedia.org/wiki/Lambda_calculus
【讨论】:
无关:您可以使用operator.itemgetter(1)
代替lambda x: x[1]
。
@J.F.塞巴斯蒂安:虽然会要求你先import operator
。 @Amber:谢谢;我不记得我的头顶所以只是谨慎行事。
@J.F.塞巴斯蒂安,是的,“operator.itemgetter”,以及数百个其他短函数,人们必须记住,每一个都有一个特定的用途——或者使用 lambda,可以在其中创建一个通用表达式,而不必准确回忆哪个简短的功能可以完成这项工作。 NOT 使用 operator.itemgetter 似乎很好地证明了 lambda,正如 OP 所要求的那样。
operator
中的函数实际上比对应的 lambda 函数快一百倍。
@NoMore:直到你遇到一些更复杂的事情,例如lambda x: x[0]**2 + x[1]**2
【参考方案2】:
语法在某些情况下更简洁,主要是在处理map
等时。
map(lambda x: x * 2, [1,2,3,4])
在我看来比:
def double(x):
return x * 2
map(double, [1,2,3,4])
我认为在这种情况下 lambda 是一个更好的选择,因为 def double
似乎与使用它的 map
几乎断开了连接。另外,我想它还有一个额外的好处,就是当你完成后函数会被丢弃。
在我看来,lambda 有一个缺点限制了它在 Python 中的用处:lambda 只能有一个表达式(即,你不能有多行)。它不能在强制使用空格的语言中工作。
另外,每当我使用 lambda 时,我都感觉很棒。
【讨论】:
列表理解可能更适合这种情况[x*2 for x in [1,2,3,4]]
。
我不知道 python(虽然它在我的学习列表中肯定很高),所以我想知道......真的有充分的理由不允许 lambda 多行-表达?为什么 lambda 表达式的主体不能只是更靠右的一个制表符之类的?
Guido 关于多行 lambda 和其他内容的博文:artima.com/weblogs/viewpost.jsp?thread=147358
@incrediman - 那时 lambda 和函数之间没有区别 - 函数仍然是一个可以通过名称引用的对象,具有多行 lambda 将是一个定义。
map((2).__mul__, [1,2,3,4])
.【参考方案3】:
对我来说,这是代码的表现力问题。在编写人们必须支持的代码时,该代码应该以尽可能简洁易懂的方式讲述一个故事。有时 lambda 表达式更复杂,有时它更直接地告诉代码行或代码块在做什么。写作时使用判断。
把它想象成一个句子的结构。什么是重要的部分(名词和动词与对象和方法等)以及应该如何为该行或代码块排序以直观地传达它在做什么。
【讨论】:
这太棒了:“代码应该以尽可能简洁和易于理解的方式讲述一个故事......把它想象成一个句子......” - 我将使用这个如果你不介意的话,在我教的下一门课上! 这是最好的答案。有时 lambda 是正确的,有时不是,但原因是为了可读性和支持。【参考方案4】:Lambda 函数在回调函数或需要一次性函数的地方最有用。 JAB 的示例非常完美 - 最好配上关键字参数 key
,但它仍然提供有用的信息。
什么时候
def key(x):
return x[1]
出现在距离 300 行以外的地方
[(1,2), (3,1), (5,10), (11,-3)].sort(key)
key 有什么作用?真的没有任何迹象。您可能会有某种猜测,特别是如果您熟悉该功能,但通常需要回过头来查看。哦,
[(1,2), (3,1), (5,10), (11,-3)].sort(lambda x: x[1])
告诉你更多。
-
Sort 将函数作为参数
该函数采用 1 个参数(并“返回”一个结果)
我正在尝试按列表中每个元素的第二个值对该列表进行排序
(如果列表是变量,因此您看不到值)此逻辑要求列表中至少包含 2 个元素。
可能还有更多信息,但仅通过使用匿名 lambda 函数而不是命名函数就可以获得大量信息。
另外它不会污染你的命名空间;)
【讨论】:
我最初的例子不是很完美,因为在 Python 3 中你必须使用键作为关键字参数(如果你离开key=
会导致 TypeError 被抛出)。我也忘记了sort
,它进行就地排序,实际上并没有返回它排序的对象,所以你必须在已经分配给变量的可变序列上使用它。
错误,不能像这样直接传递函数进行排序,需要使用like sort(key=key) or sort(key=lambda function)
这段代码在python 2(第一个参数是cmp
,不是key
)和python 3(sort
不接受任何位置参数)中都被破坏了。在这两种情况下,您都需要改用...sort(key=...)
。【参考方案5】:
是的,你是对的——这是一个结构性的选择。仅使用 lambda 表达式可能不会使您的程序更正确。它也不会让它们更可靠,这与速度无关。
这只是关于灵活性和表达的力量。就像列表理解一样。您可以完成大部分定义命名函数(可能会污染命名空间,但这又是纯粹的风格问题)。
它有助于提高可读性,您不必定义单独的命名函数,其他人必须找到、阅读并理解它所做的只是在其参数上调用方法 blah() .
当您使用它来编写创建和返回其他函数的函数时,可能会更有趣,这些函数的具体作用取决于它们的参数。这可能是参数化代码行为的一种非常简洁易读的方式。你可以表达更多有趣的想法。
但这仍然是一个结构性选择。你可以这样做。但面向对象编程也是如此;)
【讨论】:
【参考方案6】:暂时忽略我们正在谈论的具体匿名函数的细节。函数,包括匿名函数,在 Python 中是可赋值的量(几乎但不是真正的值)。像
这样的表达式map(lambda y: y * -1, range(0, 10))
明确提到了四个匿名量:-1、0、10 和 lambda 运算符的结果,以及 map
调用的隐含结果。可以在某些语言中创建匿名类型的值。所以忽略函数和数字之间的表面差异。何时使用匿名函数而不是命名函数的问题类似于何时将裸数字文字放入代码中以及何时声明 TIMES_I_WISHED_I_HAD_A_PONY
或 BUFFER_SIZE
的问题。有时使用(数字、字符串或函数)文字是合适的,有时更适合命名这样的事物并通过其名称来引用它。
参见例如。 Allen Holub 关于 Java 设计模式的挑衅性、发人深省的书;他经常使用匿名类。
【讨论】:
这是一个很好的答案;而不是简单地提供一个例子。并不是说这些示例对于理解 lambda 函数何时有用。【参考方案7】:Lambda 虽然在某些情况下很有用,但有很大的滥用潜力。 lambda 几乎总是使代码更难阅读。虽然将所有代码放在一行中可能会让人感到满意,但对于下一个必须阅读您的代码的人来说,它会很糟糕。
直接来自 PEP8
“Guido 的主要见解之一是代码的阅读频率远高于编写频率。”
【讨论】:
【参考方案8】:确实,滥用 lambda 函数通常会导致糟糕且难以阅读的代码。另一方面,当正确使用时,它会适得其反。这个帖子里已经有了很好的答案,但我遇到的一个例子是:
def power(n):
return lambda x: x**n
square = power(2)
cubic = power(3)
quadruple = power(4)
print(square(10)) # 100
print(cubic(10)) # 1000
print(quadruple(10)) # 10000
这个简化的案例可以在不使用 lambda 的情况下以许多其他方式重写。不过,我们可以通过这个示例推断出 lambda 函数如何在可能更复杂的情况和函数中增加可读性和代码重用。
【讨论】:
【参考方案9】:我学到的 lambda 函数的一种用法,在哪里没有其他好的选择,或者至少对我来说最好的是作为函数参数中的默认操作
parameter=lambda x: x
这会直接返回值,但您可以选择提供一个函数来执行转换或操作(例如打印答案,而不仅仅是返回)
在排序中用作键通常也很有用:
key=lambda x: x[field]
效果是按顺序按每个项目的第字段(基于零的记忆)元素进行排序。对于反转,您不需要 lambda,因为它更易于使用
reverse=True
通常,创建新的实函数并使用它而不是 lambda 几乎同样容易。如果人们学习了很多 Lisp 或其他函数式编程,他们也自然倾向于使用 lambda 函数,因为在 Lisp 中,函数定义由 lambda 演算处理。
【讨论】:
【参考方案10】:Lambda 是对象,而不是方法,它们不能像方法那样被调用。 例如
succ = ->(x) x+1
succ mow 拥有一个 Proc 对象,我们可以像使用其他任何对象一样使用它:
succ.call(2)
给我们一个输出 = 3
【讨论】:
OP 特别提到了python lambdas。这些是 ruby lambdas。【参考方案11】:Lambda 是匿名函数(没有名称的函数),可以分配给变量或作为参数传递给另一个函数。当您需要一小部分函数,它会不时运行一个或只运行一次时,lambda 的用处就会体现出来。无需在全局范围内编写函数或将其作为主程序的一部分,您可以在需要变量或其他函数时折腾几行代码。此外,当您在函数调用期间将函数作为参数传递给另一个函数时,您可以更改参数(匿名函数),使函数本身成为动态的。假设如果匿名函数使用其范围之外的变量,则称为闭包。这在回调函数中很有用。
【讨论】:
【参考方案12】:我想指出除了列表处理之外的另一种情况,其中 lambda 函数似乎是最佳选择:
from tkinter import *
from tkinter import ttk
def callback(arg):
print(arg)
pass
root = Tk()
ttk.Button(root, text = 'Button1', command = lambda: callback('Button 1 clicked')).pack()
root.mainloop()
如果我们把 lambda 函数放在这里,回调可能只执行一次回调。
ttk.Button(root, text = 'Button1', command = callback('Button1 clicked')).pack()
【讨论】:
【参考方案13】:还有一点是python没有switch语句。将 lambdas 与 dicts 结合起来可能是一种有效的选择。例如:
switch =
'1': lambda x: x+1,
'2': lambda x: x+2,
'3': lambda x: x+3
x = starting_val
ans = expression
new_ans = switch[ans](x)
【讨论】:
【参考方案14】:在某些情况下,将简单的东西表达为 lambda 会更清楚。例如,考虑常规排序与反向排序:
some_list = [2, 1, 3]
print sorted(some_list)
print sorted(some_list, lambda a, b: -cmp(a, b))
对于后一种情况,编写一个单独的完整函数只是为了返回一个 -cmp(a, b)
会比 lambda 产生更多的误解。
【讨论】:
不相关:您可以使用sorted(..., reverse=True)
代替sorted(..., lambda a,b: cmp(b,a))
绝对不是无关的,而且比答案所暗示的更容易理解。
请考虑问题和答案的年龄。那时我的 Python 经验中有很大一部分来自 Python 2.3 时代的 $reasons,反向参数(和排序函数)是在 2.4 中添加的。【参考方案15】:
Lambda 允许您动态创建函数。我见过的大多数示例除了创建一个在创建而不是执行时传递参数的函数外,并没有做更多的事情。或者他们通过不需要在使用前正式声明函数来简化代码。
一个更有趣的用途是动态构造一个 python 函数来评估一个直到运行时才知道的数学表达式(用户输入)。创建后,可以使用不同的参数重复调用该函数以评估表达式(假设您想绘制它)。考虑到 eval(),这甚至可能是一个糟糕的例子。这种类型的使用是“真正”的力量所在 - 动态创建更复杂的代码,而不是您经常看到的简单示例,这些示例只不过是减少了(源)代码大小。
【讨论】:
Lambda 不允许您做任何普通函数定义无法做到的事情。正如其他答案所说,您可能更喜欢 lambda,因为它使代码更清晰,但普通的“def”与 lambda 完全相同。 不,先生,不一样。您可以定义一个函数来计算 x+1,或者您也可以使用 lambda 创建一个函数来计算 x+n 并为 n 传递 1。您还可以使用相同的 lambda 创建一堆返回 x 和不同值的函数。关键是某些功能的一部分是在运行时以正常def无法完成的方式确定的。每个人似乎都认为这是一个很好的速记,当它真的可以做得更多时。【参考方案16】:你掌握了 lambda,你掌握了 python 中的快捷方式。原因如下:
data=[(lambda x:x.text)(x.extract()) for x in soup.findAll('p') ]
^1 ^2 ^3 ^4
在这里我们可以看到列表理解的 4 个部分:
1:我终于想要这个了 2:x.extract 将对 x 执行一些操作,这里它从汤中弹出元素 3:x 是可迭代的列表,它在 2 处与提取操作一起传递给 lambda 的输入 4:一些任意列表我没有找到在 lambda 中使用 2 个语句的其他方法,但是有了这个 我们可以利用 lambda 的无限潜力。
编辑:正如 juanpa 在 cmets 中指出的那样,使用 x.extract().text 完全没问题,但重点是解释 lambda 管道的使用,即将 lambda1 的输出作为输入传递给 lambda2。通过(lambda1 y:g(x))(lambda2 x:f(x))
【讨论】:
这是荒谬的。你应该做x.extract().text
我想把x.extract().text()
放在一边,但重点是解释“lambda 管道”。因为并非所有类都有可以返回属性副本的方法。 BS4 可以选择这样做,但此类功能通常不可用。以上是关于为啥使用 lambda 函数?的主要内容,如果未能解决你的问题,请参考以下文章