更可取:lambda 函数或嵌套函数 (def)?
与常规函数相比,使用 lambda 有一个优势:它们是在表达式中创建的。
有几个缺点:
- 没有名字(只有
'<lambda>')
- 没有文档字符串
- 没有注释
- 没有复杂的语句
它们也是相同类型的对象。由于这些原因,我通常更喜欢使用 def 关键字而不是 lambdas 创建函数。
第一点 - 它们是同一类型的对象
lambda 产生与常规函数相同类型的对象
>>> l = lambda: 0
>>> type(l)
<class 'function'>
>>> def foo(): return 0
...
>>> type(foo)
<class 'function'>
>>> type(foo) is type(l)
True
由于 lambda 是函数,因此它们是一流的对象。
lambda 和函数:
- 可以作为参数传递(与常规函数相同)
- 在外部函数中创建时,会成为该外部函数局部变量的闭包
但默认情况下,lambdas 缺少一些函数通过完整函数定义语法获得的东西。
兰巴的__name__ 是'<lambda>'
Lambda 毕竟是匿名函数,所以它们不知道自己的名字。
>>> l.__name__
'<lambda>'
>>> foo.__name__
'foo'
因此无法在其命名空间中以编程方式查找 lambda。
这限制了某些事情。比如foo可以用序列化代码查找,而l不能:
>>> import pickle
>>> pickle.loads(pickle.dumps(l))
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
_pickle.PicklingError: Can't pickle <function <lambda> at 0x7fbbc0464e18>:
attribute lookup <lambda> on __main__ failed
我们可以很好地查找foo - 因为它知道自己的名字:
>>> pickle.loads(pickle.dumps(foo))
<function foo at 0x7fbbbee79268>
Lambda 没有注释也没有文档字符串
基本上,没有记录 lambda。让我们重写 foo 以便更好地记录:
def foo() -> int:
"""a nullary function, returns 0 every time"""
return 0
现在,foo 有文档:
>>> foo.__annotations__
{'return': <class 'int'>}
>>> help(foo)
Help on function foo in module __main__:
foo() -> int
a nullary function, returns 0 every time
然而,我们没有相同的机制向 lambdas 提供相同的信息:
>>> help(l)
Help on function <lambda> in module __main__:
<lambda> lambda (...)
但我们可以破解它们:
>>> l.__doc__ = 'nullary -> 0'
>>> l.__annotations__ = {'return': int}
>>> help(l)
Help on function <lambda> in module __main__:
<lambda> lambda ) -> in
nullary -> 0
但是,可能有一些错误弄乱了帮助的输出。
Lambda 只能返回一个表达式
Lambda 不能返回复杂的语句,只能返回表达式。
>>> lambda: if True: 0
File "<stdin>", line 1
lambda: if True: 0
^
SyntaxError: invalid syntax
当然,表达式可能相当复杂,如果您非常努力尝试,您可能可以使用 lambda 完成相同的操作,但增加的复杂性更不利于编写清晰的代码。
为了清晰和可维护性,我们使用 Python。过度使用 lambda 可能会对此产生不利影响。
lambda 的唯一优点:可以在单个表达式中创建
这是唯一可能的好处。由于您可以使用表达式创建 lambda,因此您可以在函数调用中创建它。
在函数调用中创建一个函数避免了(廉价的)名称查找,而不是在其他地方创建的。
但是,由于 Python 是经过严格评估的,因此除了避免名称查找之外,这样做没有其他性能提升。
对于一个非常简单的表达式,我可能会选择一个 lambda。
在执行交互式 Python 时,我也倾向于使用 lambda,以避免在需要时使用多行代码。当我想在调用timeit.repeat 时将参数传递给构造函数时,我使用以下代码格式:
import timeit
def return_nullary_lambda(return_value=0):
return lambda: return_value
def return_nullary_function(return_value=0):
def nullary_fn():
return return_value
return nullary_fn
现在:
>>> min(timeit.repeat(lambda: return_nullary_lambda(1)))
0.24312214995734394
>>> min(timeit.repeat(lambda: return_nullary_function(1)))
0.24894469301216304
我相信上面的细微时差可以归因于return_nullary_function 中的名称查找 - 请注意,它非常可以忽略不计。
结论
Lambda 适用于您希望尽量减少代码行数以支持创建奇异点的非正式情况。
Lambda 不适用于更正式的情况,在这种情况下,您需要稍后来的代码编辑人员清楚地了解,尤其是在它们不重要的情况下。
我们知道我们应该给我们的对象起个好名字。当对象有 no 名称时,我们如何做到这一点?
出于所有这些原因,我通常更喜欢使用 def 而不是 lambda 创建函数。