Python中的lambda
关键字可用来快速声明小型匿名函数。lambda函数的行为与使用def
关键字声明的常规函数一样,可以用于所有需要函数对象的地方。
下面定义一个简单的lambda函数,用于进行加法运算:
>>> add = lambda x, y: x + y
>>> add(5, 3)
8
用def
关键字能声明相同的add
函数,但稍微冗长一些:
>>> def add(x, y):
... return x + y
>>> add(5, 3)
8
你可能想知道lambda有什么独特之处:如果只是比用def
声明函数稍微方便一点,那有什么大不了的?
来看下面的例子,同时脑海里要记着函数表达式这个概念:
>>> (lambda x, y: x + y)(5, 3)
8
发生了什么呢?这里用lambda
内联定义了一个加法函数,然后立即用参数5
和3
进行调用。
从概念上讲,lambda表达式lambda x,y:x + y
与用def
声明函数相同,但从语法上来说表达式位于lambda内部。两者的关键区别在于,lambda不必先将函数对象与名称绑定,只需在lambda中创建一个想要执行的表达式,然后像普通函数那样立即调用进行计算。
在继续学习之前,最好先自己尝试一下前面的代码示例来深入理解其中的含义。我自己当初花了不少时间才掌握这些内容。如果你在理解这些知识的时候花费了一点时间,不用担心,这是值得的。
lambda和普通函数定义之间还有另一个语法差异。lambda函数只能含有一个表达式,这意味着lambda函数不能使用语句或注解(annotation),甚至不能使用返回语句。
那么应该如何从lambda返回值呢?执行lambda函数时会计算其中的表达式,然后自动返回表达式的结果,所以其中总是有一个隐式的返回表达式。因此有些人把lambda称为单表达式函数。
Python lambda函数 lambda的使用场景
应该在什么时候使用lambda函数呢?从技术上讲,每当需要提供一个函数对象时,就可以使用lambda表达式。而且,因为lambda是匿名的,所以不需要先分配一个名字。
因此,lambda能方便灵活地快速定义Python函数。我一般在对可迭代对象进行排序时,使用lambda表达式定义简短的key
函数:
>>> tuples = [(1, 'd'), (2, 'b'), (4, 'a'), (3, 'c')]
>>> sorted(tuples, key=lambda x: x[1])
>[(4, 'a'), (2, 'b'), (3, 'c'), (1, 'd')]
上面的例子按照每个元组中的第2个值对元组列表进行排序。在这种情况下,用lambda函数能快速修改排序顺序。下面是另一个排序示例:
>>> sorted(range(-5, 6), key=lambda x: x * x)
[0, -1, 1, -2, 2, -3, 3, -4, 4, -5, 5]
前面展示的两个示例在Python内部都有更简洁的实现,分别是operator.itemgetter()
和abs()
函数。但我希望你能从中看出lambda带来的灵活性。想要根据某个键值的计算结果对序列排序?没问题,现在你应该知道怎么做了。
lambda还有一个有趣之处:与普通的嵌套函数一样,lambda也可以像词法闭包那样工作。
词法闭包是什么?这只是对某种函数的一个奇特称呼,该函数能记住来自某个封闭词法作用域的值,即使程序流已经不在作用域中也不例外。下面用一个(相当学术的)例子来演示这个思想:
>>> def make_adder(n):
... return lambda x: x + n
>>> plus_3 = make_adder(3)
>>> plus_5 = make_adder(5)
>>> plus_3(4)
7
>>> plus_5(4)
9
在上面的例子中,即使n
是在make_adder
函数(封闭的作用域)中定义的,但x + n
lambda仍然可以访问n
的值。
有时,与def
关键字声明的嵌套函数相比,lambda函数可以更清楚地表达程序员的意图。不过说实话,lambda的应用并不广泛,至少我在写代码时很少用,所以下面再多说几句。
Python lambda函数 不应过度使用lambda
虽然我希望本节能激发你探索lambda函数的兴趣,但现在是时候告诫你,应该非常小心谨慎地使用lambda函数。
若工作代码用到了lambda,虽然看起来很“酷”,但实际上对自己和同事都是一种负担。如果想使用lambda表达式,那么请花几秒(或几分钟)思考,为了获得你所期望的结果,这种方式是否真的最简洁且最易维护。
例如,用下面这种方式少写两行代码很蠢。虽然在技术上可行且足够花哨,但会让那些工期很紧并且需要快速修复bug的人觉得难以理解:
# 有害:
>>> class Car:
... rev = lambda self: print('Wroom!')
... crash = lambda self: print('Boom!')
>>> my_car = Car()
>>> my_car.crash()
'Boom!'
将lambda和map()
或filter()
结合起来构建复杂的表达式也很难让人理解,此时用列表解析式或生成器表达式通常会清晰不少:
# 有害:
>>> list(filter(lambda x: x % 2 == 0, range(16)))
[0, 2, 4, 6, 8, 10, 12, 14]
# 清晰:
>>> [x for x in range(16) if x % 2 == 0]
[0, 2, 4, 6, 8, 10, 12, 14]
如果你发现自己在用lambda表达式做非常复杂的事,那么可以考虑定义一个有恰当名称的独立函数。
从长远来看,少敲一些代码并不重要,同事(以及未来的自己)并不喜欢花哨的炫耀,而是喜欢清晰可读的代码。
Python lambda函数 关键要点
-
lambda函数是单表达式函数,不必与名称绑定(匿名)。
-
lambda函数不能使用普通的Python语句,其中总是包含一个隐式
return
语句。 -
使用前总是先问问自己:使用普通具名函数或者列表解析式是否更加清晰?