哪个更适合在Python中使用:lambda函数或嵌套函数('def')?
我主要使用lambda函数,但有时使用似乎提供相同行为的嵌套函数。
这里有一些微不足道的例子,他们在function上做同样的事情,如果在另一个函数中发现:
Lambda函数
>>> a = lambda x : 1 + x >>> a(5) 6
嵌套function
>>> def b(x): return 1 + x >>> b(5) 6
使用其中一个有好处吗? (性能?可读性?限制?一致性?等)它甚至重要吗? 如果这样做没有违反Pythonic原则: “应该有一个,最好只有一个 – 明显的方式来做到这一点” 。
如果您需要将lambda
分配给名称,请改为使用def
。 def
只是一个赋值语句糖,所以结果是一样的,而且它们更加灵活可读。
lambda
s可以使用一次,扔掉没有名字的函数。
不过,这个用例非常less见。 你很less需要传递未命名的函数对象。
内build的map()
和filter()
需要函数对象,但列表推导和生成器expression式通常比这些函数更具可读性,并且可以涵盖所有用例,而不需要lambdaexpression式。
对于你确实需要一个小函数对象的情况,你应该使用operator.add
而不是lambda x, y: x + y
如果你仍然需要一些lambda
没有覆盖,你可以考虑写一个def
,只是为了更可读。 如果函数比operator
模块更复杂,则def
可能更好。
所以,现实世界中好的lambda
用例是非常罕见的。
实际上,对我来说有两个区别:
首先是关于他们做什么和他们回报什么:
-
def是一个关键字,它不返回任何东西,并在本地命名空间中创build一个“名称”。
-
lambda是一个返回一个函数对象的关键字,并不会在本地命名空间中创build一个“名称”。
因此,如果你需要调用一个带有函数对象的函数,那么在一行python代码中做的唯一方法就是使用lambdaexpression式。 def没有等价物。
在一些框架中,这实际上是相当普遍的; 例如,我使用了Twisted ,做了一些类似的事情
d.addCallback(lambda result: setattr(self, _someVariable, result))
是相当普遍的,而且更加简洁。
第二个区别是关于实际function被允许做什么。
- 用'def'定义的函数可以包含任何python代码
- 用'lambda'定义的函数必须求值为一个expression式,因此不能包含print,import,raise等语句。
例如,
def p(x): print x
按预期工作,而
lambda x: print x
是一个SyntaxError。
当然,还有一些解决方法 – 用sys.stdout.write
replaceprint
,或者用__import__
import
。 但是通常情况下,你最好在这种情况下使用一个函数。
在这次采访中, Guido van Rossum说他希望他没有让'lambda'进入Python:
“ Q.你最不喜欢Python的哪个特性?
有时我接受稿件太快了,后来才意识到这是一个错误。 一个例子是一些函数式编程特性,比如lambda函数。 lambda是一个关键字,可以让你创build一个小的匿名函数; 内置函数(如map,filter和reduce)在序列types(如列表)上运行函数。
实际上,结果并不好。 Python只有两个范围:本地和全局。 这使得编写lambda函数变得很痛苦,因为你经常要访问定义了lambda的范围内的variables,但是你不能因为这两个范围。 有一种解决办法,但这是一个混乱的东西。 Python中似乎更容易使用for循环,而不是乱用lambda函数。 地图和朋友只有在已经有内置function的时候才能正常工作。
恕我直言,Iambdas有时可以方便,但通常是方便的,可读性的代价。 你能告诉我这是什么吗?
str(reduce(lambda x,y:x+y,map(lambda x:x**x,range(1,1001))))[-10:]
我写了,花了一分钟才弄明白。 这是从欧拉项目 – 我不会说哪个问题,因为我讨厌破坏者,但它运行在0.124秒:)
对于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
我同意nosklo的build议:如果你需要给函数一个名字,使用def
。 我保留lambda
函数的情况下,我只是简单的代码片段传递给另一个函数,例如:
a = [ (1,2), (3,4), (5,6) ] b = map( lambda x: x[0]+x[1], a )
性能:
用lambda
创build一个函数要比用def
创build它要快一些。 区别在于def
在locals表中创build一个名字条目。 由此产生的function具有相同的执行速度。
可读性:
对于大多数Python用户来说,Lambda函数的可读性稍差,但在某些情况下也更加简洁。 考虑从使用非function转换到function例程:
# Using non-functional version. heading(math.sqrt(vx * vx + vy * vy), math.atan(vy / vx)) # Using lambda with functional version. fheading(v, lambda v: math.sqrt(vx * vx + vy * vy), lambda v: math.atan(vy / vx)) # Using def with functional version. def size(v): return math.sqrt(vx * vx + vy * vy) def direction(v): return math.atan(vy / vx) deal_with_headings(v, size, direction)
正如您所看到的, lambda
版本更短,更简单,因为您只需要将lambda v:
添加到原始非function版本即可转换为function版本。 它也更加简洁。 但是请记住,很多Python用户会被lambda语法弄糊涂,所以在长度和实际复杂度上的损失可能会被同行编码人员混淆。
限制:
-
lambda
函数只能使用一次,除非分配给一个variables名称。 - 分配给variables名称的
lambda
函数没有def
函数的优势。 -
lambda
function可能难以或不可能腌制。 -
def
函数的名字必须仔细select,以便合理的描述性和独特性,或者至less在其他范围内不被使用。
一致性:
Python主要避免函数式编程惯例,而采用程序性和简单的客观语义。 lambda
操作符与这种偏见形成鲜明对比。 而且,作为已经普遍使用的def
的替代, lambda
函数增加了语法的多样性。 有些人会认为这不太一致。
预先存在的function:
正如其他人所指出的那样, lambda
在现场的许多用途可以由operator
或其他模块的成员来代替。 例如:
do_something(x, y, lambda x, y: x + y) do_something(x, y, operator.add)
在许多情况下,使用预先存在的函数可以使代码更具可读性。
Pythonic原理:“应该有一个,最好只有一个明显的方法来做到这一点”
这与真理原理的单一来源类似。 不幸的是,单一显而易见的做法原则一直是Python的渴望,而不是真正的指导原则。 考虑Python中非常强大的数组理解。 它们在function上等同于map
和filter
function:
[e for e in some_array if some_condition(e)] filter(some_array, some_condition)
lambda
和def
是一样的。
这是一个意见的问题,但我会说,Python语言中的任何打算用于一般用途而不会明显破坏任何东西的东西是“Pythonic”就够了。
lambda的主要用途一直是简单的callback函数,而map,reduce,filter则需要函数作为参数。 列表理解成为常态,并且允许添加,如下所示:
x = [f for f in range(1, 40) if f % 2]
很难想象在日常使用中使用lambda的真实情况。 因此,我会说,避免lambda和创build嵌套函数。
在同意其他答案的同时,有时更具可读性。 下面是一个lambda
派上用场的例子,在用例中我遇到了一个N维的defaultdict
。
这是一个例子:
from collections import defaultdict d = defaultdict(lambda: defaultdict(list)) d['Foo']['Bar'].append(something)
我发现它比为第二维创build一个def
更可读。 这对于更高的尺寸来说更为重要。
我find的lambdas的一个用法是在debugging消息中。
由于lambda可以被懒惰评估,你可以有这样的代码:
log.debug(lambda: "this is my message: %r" % (some_data,))
而不是可能是昂贵的:
log.debug("this is my message: %r" % (some_data,))
即使debugging调用由于当前日志logging级别而没有生成输出,它也会处理格式string。
当然,如上所述,正在使用的日志logging模块必须支持lambdas作为“懒惰参数”(就像我的日志logging模块一样)。
对于按需内容价值创造的任何其他懒惰评估情况,可以应用相同的想法。
例如这个自定义的三元运算符:
def mif(condition, when_true, when_false): if condition: return when_true() else: return when_false() mif(a < b, lambda: a + a, lambda: b + b)
代替:
def mif(condition, when_true, when_false): if condition: return when_true else: return when_false mif(a < b, a + a, b + b)
与lambdas只有通过条件select的expression式将被评估,没有lambda将被评估。
当然,你可以简单地使用函数来代替lambdaexpression式,但是对于简短的expression式,lambdas是(c)精简的。
lambda的一个重要限制是它们除了expression式之外不能包含任何东西。 lambdaexpression式几乎不可能产生除了不重要的副作用以外的任何东西,因为它不能具有像定义函数一样丰富的身体。
这就是说,Lua影响了我的编程风格,广泛使用了匿名函数,并且把代码与他们混淆了。 最重要的是,我倾向于将map / reduce看作是抽象运算符,而不考虑列表parsing或生成器,几乎就像我使用这些运算符明确推迟实现决定一样。
编辑:这是一个很老的问题,我对这件事的看法有所改变。
首先,我强烈反对将lambda
expression式分配给一个variables; 因为Python有一个特殊的语法(提示, def
)。 除此之外,lambda的许多用途,即使没有得到名字,也有预定义的(和更高效的)实现。 例如,所讨论的例子可以缩写为(1).__add__
,而不需要将其包含在lambda
或def
。 operator
, itertools
和functools
模块的一些组合可以满足其他许多常见的用途。
我同意nosklo。 顺便说一句,即使有一次使用,扔掉function,大部分时间你只是想使用操作模块的东西。
EG:
你有这个签名的函数:myFunction(数据,callback函数)。
你想传递一个添加2个元素的函数。
使用lambda:
myFunction(data, (lambda x, y : x + y))
pythonic方式:
import operator myFunction(data, operator.add)
当然,这是一个简单的例子,但是运营商模块提供了很多东西,包括列表和字典的设置器/获取器。 真的很酷。
如果你只是将lambda分配给本地范围内的一个variables,那么你可以使用def,因为它更具可读性,将来可以更容易地扩展:
fun = lambda a, b: a ** b # a pointless use of lambda map(fun, someList)
要么
def fun(a, b): return a ** b # more readable map(fun, someList)
lambda有用于生成新的函数:
def somefunc(x): return lambda y: x+y f = somefunc(10) f(2) >>> 12 f(4) >>> 14
主要区别在于你不能使用内联def
函数,这在我看来是lambda
函数最方便的用例。 例如,sorting对象列表时:
my_list.sort(key=lambda o: ox)
因此,我build议保持lambda的使用这种平凡的操作,这也没有真正受益于命名该函数提供的自动文档。