4.7. 使用 lambda 函数

Python 支持一种有趣的语法,允许您动态定义单行迷你函数。 这些所谓的 lambda 函数是从 Lisp 借鉴而来的,可以在任何需要函数的地方使用。

示例 4.20. 介绍 lambda 函数

>>> def f(x):
...     return x*2
...     
>>> f(3)
6
>>> g = lambda x: x*2  1
>>> g(3)
6
>>> (lambda x: x*2)(3) 2
6
1 这是一个 lambda 函数,它完成与其上面的普通函数相同的功能。 请注意这里的简写语法:参数列表周围没有括号,并且缺少 return 关键字(它是隐含的,因为整个函数只能是一个表达式)。 此外,该函数没有名称,但可以通过分配给它的变量来调用。
2 您甚至可以在不将 lambda 函数分配给变量的情况下使用它。 这可能不是世界上最有用的东西,但这只是表明 lambda 只是一个内联函数。

概括地说,lambda 函数是一个接受任意数量参数(包括 可选参数)并返回单个表达式值的函数。 lambda 函数不能包含命令,并且它们不能包含多个表达式。 不要试图将太多内容塞进 lambda 函数中;如果您需要更复杂的东西,请定义一个普通的函数,并根据需要将其延长。

Note
lambda 函数是一个风格问题。 使用它们从来都不是必需的;在您可以使用它们的任何地方,您都可以定义一个单独的普通函数并改用它。 我在想要封装特定的、不可重复使用的代码而不希望我的代码中充斥着许多小的一行函数的地方使用它们。

4.7.1. 真实世界中的 lambda 函数

以下是 apihelper.py 中的 lambda 函数

    processFunc = collapse and (lambda s: " ".join(s.split())) or (lambda s: s)

请注意,这使用了 and-or 技巧的简单形式,这是可以的,因为 lambda 函数在 布尔上下文中 始终为真。 (这并不意味着 lambda 函数不能返回假值。 该函数始终为真;其返回值可以是任何值。)

还要注意,您正在使用不带参数的 split 函数。 您已经看到它与 一个或两个参数 一起使用,但是如果不带任何参数,它将在空格处拆分。

示例 4.21. 不带参数的 split

>>> s = "this   is\na\ttest"  1
>>> print s
this   is
a	test
>>> print s.split()           2
['this', 'is', 'a', 'test']
>>> print " ".join(s.split()) 3
'this is a test'
1 这是一个多行字符串,由转义字符而不是 三引号 定义。 \n 是回车符,\t 是制表符。
2 不带任何参数的 split 会在空格处拆分。 所以三个空格、一个回车符和一个制表符都是一样的。
3 您可以通过使用 split 拆分字符串,然后使用 join 将其重新连接起来,并使用单个空格作为分隔符来规范化空格。 这就是 info 函数将多行 docstring 合并为一行的作用。

那么 info 函数实际上对这些 lambda 函数、splitand-or 技巧做了些什么呢?

    processFunc = collapse and (lambda s: " ".join(s.split())) or (lambda s: s)

processFunc 现在是一个函数,但它是什么函数取决于 collapse 变量的值。 如果 collapse 为真,则 processFunc(string) 将折叠空格;否则,processFunc(string) 将返回其参数不变。

要在像 Visual Basic 这样不太健壮的语言中做到这一点,您可能会创建一个函数,该函数接受一个字符串和一个 collapse 参数,并使用 if 语句来决定是否折叠空格,然后返回适当的值。 这将是低效的,因为该函数需要处理所有可能的情况。 每次调用它时,它都需要先决定是否折叠空格,然后才能为您提供您想要的东西。 在 Python 中,您可以将该决策逻辑从函数中取出,并定义一个 lambda 函数,该函数是专门定制的,可以准确地(并且仅)为您提供您想要的东西。 这更高效、更优雅,并且不太容易出现那些讨厌的“哦,我以为那些论点是颠倒的”之类的错误。

关于 lambda 函数的更多阅读