哪个更适合在Python中使用:lambda函数或嵌套函数('def')?

Ray*_*ega 95 python syntax lambda function

我主要使用lambda函数,但有时使用似乎提供相同行为的嵌套函数.

这里有一些简单的例子,如果在另一个函数中找到它们,它们在功能上做同样的事情:

Lambda函数

>>> a = lambda x : 1 + x
>>> a(5)
6
Run Code Online (Sandbox Code Playgroud)

嵌套功能

>>> def b(x): return 1 + x

>>> b(5)
6
Run Code Online (Sandbox Code Playgroud)

使用一个优于另一个是否有优势?(性能?可读性?限制?一致性?等)

它甚至重要吗?如果不这样做那就违反了Pythonic原则:

"应该有一种 - 最好只有一种 - 显而易见的方法".

nos*_*klo 94

如果您需要将lambda名称指定给名称,请使用def替代名称.defs只是赋值的语法糖,所以结果是一样的,它们更灵活,更易读.

lambdas可以使用一次,扔掉没有名字的函数.

但是,这个用例非常罕见.您很少需要传递未命名的函数对象.

内置map()filter()需要函数对象,但是列表推导生成器表达式通常比那些函数更具可读性,并且可以覆盖所有用例,而不需要lambdas.

对于你真的需要一个小函数对象的情况,你应该使用operator模块函数,operator.add而不是lambda x, y: x + y

如果你仍然需要一些lambda不被覆盖的东西,你可以考虑写一个def,只是为了更具可读性.如果函数比operator模块上的函数更复杂,def则可能更好.

所以,现实世界的好用lambda例非常罕见.

  • 我同意何时使用`lambda`的答案,但我不同意这是"非常罕见",对于`sorted`或`itertools.groupby`等关键函数来说很常见,例如`sorted(['a1 ','b0'],key = lambda x:int(x [1]))` (5认同)

Tho*_*ele 30

实际上,对我来说有两点不同:

第一个是关于他们做什么以及他们返回什么:

  • def是一个不返回任何内容的关键字,并在本地名称空间中创建"名称".

  • lambda是一个返回函数对象的关键字,不会在本地名称空间中创建"名称".

因此,如果你需要调用一个带有函数对象的函数,在一行python代码中执行该操作的唯一方法是使用lambda.没有与def相当的东西.

在某些框架中,这实际上很常见; 例如,我使用Twisted很多,所以做了类似的事情

d.addCallback(lambda result: setattr(self, _someVariable, result))
Run Code Online (Sandbox Code Playgroud)

很常见,而且对lambdas更简洁.

第二个区别是关于允许实际功能的内容.

  • 用'def'定义的函数可以包含任何python代码
  • 用'lambda'定义的函数必须求值为表达式,因此不能包含print,import,raise等语句.

例如,

def p(x): print x
Run Code Online (Sandbox Code Playgroud)

按预期工作,而

lambda x: print x
Run Code Online (Sandbox Code Playgroud)

是一个SyntaxError.

当然,也有变通方法-代替printsys.stdout.write,或import__import__.但在这种情况下,通常你最好使用一个函数.


Chr*_*lor 21

在这次采访中, Guido van Rossum说他希望他不要让'lambda'进入Python:

" 问:你最不满意Python的哪些特性?

有时候我太快接受了贡献,后来才意识到这是一个错误.一个例子就是一些函数编程功能,比如lambda函数.lambda是一个允许您创建小型匿名函数的关键字;内置函数(如map,filter和reduce)在序列类型(如列表)上运行函数.

在实践中,它并没有那么好.Python只有两个范围:本地和全局.这使得编写lambda函数变得很痛苦,因为你经常想要在定义lambda的范围内访问变量,但是由于这两个范围你不能.有一种解决方法,但它是一种kludge.通常,在Python中使用for循环而不是乱用lambda函数似乎更容易.地图和朋友只有在已有内置功能完成您想要的功能时才能正常工作.

恕我直言,Iambdas有时候很方便,但通常以牺牲可读性为代价.你能告诉我这是做什么的:

str(reduce(lambda x,y:x+y,map(lambda x:x**x,range(1,1001))))[-10:]
Run Code Online (Sandbox Code Playgroud)

我写了它,我花了一分钟才弄明白.这是来自项目欧拉 - 我不会说哪个问题因为我讨厌剧透,但它在0.124秒内运行:)

  • 请注意,访谈相当陈旧,Python早就添加了嵌套范围,这使得他对lambda的论证不再具有相关性.我确定他仍然感到遗憾lambda,但还不足以在Python 3.0中删除它. (16认同)
  • 真的你的例子应该是反对单行的论据,而不是lambdas.此外,您应该使用内置求和函数而不是使用lambda:str(sum(map(lambda x:x**x,range(1001))))[: - 10] (9认同)
  • @ThomasWouters:我知道在 3.0 中没有删除 `lambda` 是一件很接近的事情,而且 Guido 并没有努力保留它。 (2认同)

And*_*den 9

对于n = 1000,这里有一个调用函数vs lambda的时间:

In [11]: def f(a, b):
             return a * b

In [12]: g = lambda x, y: x * y

In [13]: %%timeit -n 100
for a in xrange(n):
  for b in xrange(n):
    f(a, b)
   ....:
100 loops, best of 3: 285 ms per loop

In [14]: %%timeit -n 100
for a in xrange(n):
  for b in xrange(n):
    g(a, b)
   ....:
100 loops, best of 3: 298 ms per loop

In [15]: %%timeit -n 100
for a in xrange(n):
  for b in xrange(n):
    (lambda x, y: x * y)(a, b)
   ....:
100 loops, best of 3: 462 ms per loop
Run Code Online (Sandbox Code Playgroud)

  • 有趣的是看到lambda和定义版本大致相同.最后一次测试需要更多时间,因为python可能需要在每次定义lambda函数时分配空间. (3认同)

Aar*_*all 9

更可取的是:lambda 函数还是嵌套函数 ( def)?

与常规函数相比,使用 lambda 有一个优点:它们是在表达式中创建的。

有几个缺点:

  • 没有名字(只是'<lambda>'
  • 没有文档字符串
  • 没有注释
  • 没有复杂的陈述

它们也是相同类型的对象。由于这些原因,我通常更喜欢使用def关键字而不是 lambda来创建函数。

第一点 - 它们是相同类型的对象

lambda 产生与常规函数相同类型的对象

>>> l = lambda: 0
>>> type(l)
<class 'function'>
>>> def foo(): return 0
... 
>>> type(foo)
<class 'function'>
>>> type(foo) is type(l)
True
Run Code Online (Sandbox Code Playgroud)

由于 lambda 是函数,因此它们是一流的对象。

lambdas 和函数:

  • 可以作为参数传递(与常规函数相同)
  • 当在外部函数中创建时成为该外部函数局部变量的闭包

但是默认情况下,lambdas 缺少函数通过完整函数定义语法获得的一些东西。

兰巴__name__'<lambda>'

Lambda 毕竟是匿名函数,所以它们不知道自己的名字。

>>> l.__name__
'<lambda>'
>>> foo.__name__
'foo'
Run Code Online (Sandbox Code Playgroud)

因此,无法在其命名空间中以编程方式查找 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
Run Code Online (Sandbox Code Playgroud)

我们可以foo很好地查找——因为它知道自己的名字:

>>> pickle.loads(pickle.dumps(foo))
<function foo at 0x7fbbbee79268>
Run Code Online (Sandbox Code Playgroud)

Lambda 没有注释和文档字符串

基本上,没有记录 lambdas。让我们重写foo以更好地记录:

def foo() -> int:
    """a nullary function, returns 0 every time"""
    return 0
Run Code Online (Sandbox Code Playgroud)

现在, foo 有文档:

>>> foo.__annotations__
{'return': <class 'int'>}
>>> help(foo)
Help on function foo in module __main__:

foo() -> int
    a nullary function, returns 0 every time
Run Code Online (Sandbox Code Playgroud)

然而,我们没有相同的机制来向 lambdas 提供相同的信息:

>>> help(l)
Help on function <lambda> in module __main__:

<lambda> lambda (...)
Run Code Online (Sandbox Code Playgroud)

但我们可以破解它们:

>>> l.__doc__ = 'nullary -> 0'
>>> l.__annotations__ = {'return': int}
>>> help(l)
Help on function <lambda> in module __main__:

<lambda> lambda ) -> in
    nullary -> 0
Run Code Online (Sandbox Code Playgroud)

但是可能有一些错误弄乱了帮助的输出。

Lambda 只能返回一个表达式

Lambda 不能返回复杂的语句,只能返回表达式。

>>> lambda: if True: 0
  File "<stdin>", line 1
    lambda: if True: 0
             ^
SyntaxError: invalid syntax
Run Code Online (Sandbox Code Playgroud)

诚然,表达式可能相当复杂,如果你非常努力,你可能可以用 lambda 来完成同样的事情,但增加的复杂性更不利于编写清晰的代码。

为了清晰和可维护性,我们使用 Python。过度使用 lambdas 可以解决这个问题。

lambda的唯一优点:可以在单个表达式中创建

这是唯一可能的好处。由于您可以使用表达式创建 lambda,因此您可以在函数调用中创建它。

在函数调用中创建函数可以避免(廉价的)名称查找,而不是在其他地方创建的名称查找。

但是,由于 Python 是经过严格评估的,因此除了避免名称查找之外,这样做没有其他性能提升。

对于非常简单的表达式,我可能会选择 lambda。

在进行交互式 Python 时,我也倾向于使用 lambdas,以避免多行。当我想在调用时将参数传递给构造函数时,我使用以下类型的代码格式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
Run Code Online (Sandbox Code Playgroud)

现在:

>>> min(timeit.repeat(lambda: return_nullary_lambda(1)))
0.24312214995734394
>>> min(timeit.repeat(lambda: return_nullary_function(1)))
0.24894469301216304
Run Code Online (Sandbox Code Playgroud)

我相信上面的微小的时间差可以归因于在名称查找return_nullary_function-注意,这是非常微不足道的。

结论

Lambda 非常适合非正式情况,在这种情况下,您希望尽量减少代码行数以支持创建奇异点。

Lambdas 不适合更正式的情况,在这种情况下,您需要为稍后的代码编辑器提供清晰的信息,尤其是在它们不重要的情况下。

我们知道我们应该给我们的对象起个好名字。当对象没有名称时,我们如何做到这一点?

由于所有这些原因,我通常更喜欢创建函数 withdef而不是 with lambda


Dan*_*ski 6

我同意nosklo的建议:如果你需要给这个函数命名,请使用def.我保留lambda函数用于我只是将一小段代码传递给另一个函数的情况,例如:

a = [ (1,2), (3,4), (5,6) ]
b = map( lambda x: x[0]+x[1], a )
Run Code Online (Sandbox Code Playgroud)

  • 在map/lambda的大多数组合中,您可以使用列表推导或更合适的函数替换它.例如,"map(sum,a)"或"[x [0] + x [1] for x in a"" (2认同)

Jon*_*han 5

虽然同意其他答案,但有时它更具可读性。这是一个lambda派上用场的示例,在我不断遇到 N 维的用例中defaultdict
下面是一个例子:

from collections import defaultdict
d = defaultdict(lambda: defaultdict(list))
d['Foo']['Bar'].append(something)
Run Code Online (Sandbox Code Playgroud)

我发现它比def为第二维创建 a 更具可读性。这对于更高的维度更为重要。


Pi *_*ion 5

性能:

以创建一个功能lambda速度稍快比创建它def.不同之处def在于在locals表中创建了一个名称条目.结果函数具有相同的执行速度.


可读性:

对于大多数Python用户来说,Lambda函数的可读性稍差,但在某些情况下也更简洁.考虑从使用非功能性转换为功能性例程:

# Using non-functional version.

heading(math.sqrt(v.x * v.x + v.y * v.y), math.atan(v.y / v.x))

# Using lambda with functional version.

fheading(v, lambda v: math.sqrt(v.x * v.x + v.y * v.y), lambda v: math.atan(v.y / v.x))

# Using def with functional version.

def size(v):
    return math.sqrt(v.x * v.x + v.y * v.y)

def direction(v):
    return math.atan(v.y / v.x)

deal_with_headings(v, size, direction)
Run Code Online (Sandbox Code Playgroud)

正如您所看到的,lambda版本更短且更"简单",因为您只需要添加lambda v:到原始的非功能版本即可转换为功能版本.它也更简洁.但是请记住,许多Python用户会被lambda语法所迷惑,所以你失去的长度和真正的复杂性可能会被其他编码人员混淆.


限制:

  • lambda 函数只能使用一次,除非分配给变量名.
  • lambda分配给变量名的def函数没有函数优势.
  • lambda 功能可能很难或不可能发泡.
  • def 必须仔细选择功能的名称,使其具有合理的描述性和独特性,或至少在范围内不使用.

一致性:

Python主要避免使用函数式编程约定,而采用程序性和简单的客观语义.该lambda操作员站直接的对比这种偏见.此外,作为已经流行的替代方案def,该lambda函数为您的语法增加了多样性.有人会认为不太一致.


预先存在的功能:

如其他人所指出的lambda,本领域的许多用途可以由该operator模块或其他模块的成员代替.例如:

do_something(x, y, lambda x, y: x + y)
do_something(x, y, operator.add)
Run Code Online (Sandbox Code Playgroud)

使用预先存在的函数可以使代码在许多情况下更具可读性.


Pythonic原则:"应该有一个 - 最好只有一个 - 显而易见的方法"

这与真理学说的单一来源相似.不幸的是,单一显而易见的原则一直是Python的渴望,而不是真正的指导原则.考虑一下Python中非常强大的数组解析.它们在功能上等同于mapfilter功能:

[e for e in some_array if some_condition(e)]
filter(some_array, some_condition)
Run Code Online (Sandbox Code Playgroud)

lambda并且def是一样的.

这是一个意见问题,但我会说,用于一般用途的Python语言中没有明显破坏任何东西的东西都是"Pythonic".