问题:使用哪个更合适:lambda函数或嵌套函数(’def’)?
我主要使用lambda函数,但有时使用似乎提供相同行为的嵌套函数。
这是一些琐碎的示例,如果在另一个函数中找到它们,它们在功能上会做同样的事情:
Lambda函数
>>> a = lambda x : 1 + x
>>> a(5)
6
嵌套功能
>>> def b(x): return 1 + x
>>> b(5)
6
使用一个相对于另一个有优势吗?(性能?可读性?局限性?一致性?等)
有关系吗 如果不这样做,那确实违反了Python原则:
回答 0
如果需要将分配lambda
给名称,请改用a def
。def
s只是分配的语法糖,因此结果是相同的,并且它们更具灵活性和可读性。
lambda
s只能使用一次,丢弃没有名称的函数。
但是,这种用例很少见。您很少需要传递未命名的函数对象。
内建函数map()
和filter()
需要函数对象,但是列表理解和生成器表达式通常比那些函数更具可读性,并且可以覆盖所有用例,而无需使用lambda。
对于情况下,你真的需要一个小的函数对象,你应该使用operator
模块的功能,如operator.add
代替lambda x, y: x + y
如果您仍然需要一些lambda
未涵盖的内容,则可以考虑编写def
,以提高可读性。如果功能比operator
模块中的功能更复杂,则a def
可能更好。
因此,现实世界中的好用lambda
例非常少见。
回答 1
实际上,对我来说有两个区别:
首先是关于他们做什么以及他们返回什么:
def是不返回任何内容并在本地命名空间中创建“名称”的关键字。
lambda是一个关键字,它返回一个函数对象,并且不在本地命名空间中创建“名称”。
因此,如果您需要调用带有函数对象的函数,则在一行python代码中执行此操作的唯一方法是使用lambda。def没有等效功能。
在某些框架中,这实际上很常见。例如,我经常使用Twisted,
d.addCallback(lambda result: setattr(self, _someVariable, result))
是很常见的,并且对lambda更为简洁。
第二点区别是允许实际执行的功能。
- 用’def’定义的函数可以包含任何python代码
- 用“ lambda”定义的函数必须求值为表达式,因此不能包含诸如print,import,raise,…之类的语句。
例如,
def p(x): print x
如预期般运作
lambda x: print x
是一个SyntaxError。
当然,也有变通方法-代替print
用sys.stdout.write
,或import
用__import__
。但是通常情况下,最好还是使用一个函数。
回答 2
Guido van Rossum 在这次采访中说,他希望自己不要让“ lambda”进入Python:
“ 问:您最不满意Python的什么功能?
有时我太快地接受了贡献,后来才意识到这是一个错误。一个例子就是一些函数式编程功能,例如lambda函数。lambda是一个关键字,可让您创建一个小的匿名函数;内置函数(例如map,filter和reduce)可在序列类型(例如列表)上运行该函数。
在实践中,结果并非如此。Python只有两个范围:本地和全局。这使编写lambda函数很痛苦,因为您经常想在lambda定义的作用域中访问变量,但由于这两个作用域而不能。有办法解决这个问题,但这有点不合时宜。在Python中,通常只使用for循环而不是搞乱lambda函数似乎容易得多。只有当已有内置功能可以满足您的需求时,地图和朋友才能正常工作。
恕我直言,Iambdas有时可能很方便,但是通常以可读性为代价很方便。你能告诉我这是怎么做的:
str(reduce(lambda x,y:x+y,map(lambda x:x**x,range(1,1001))))[-10:]
我写了它,花了我一分钟才弄清楚。这是来自欧拉计划-我不会说哪个问题,因为我讨厌剧透,但是它只需要0.124秒即可:)
回答 3
对于n = 1000,这是调用函数与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
回答 4
性能:
创建一个功能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)
如您所见,在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)
在许多情况下,使用预先存在的功能可以使代码更具可读性。
Python原则:“应该有一种-最好只有一种-显而易见的方法”
这类似于真理教义的单一来源。不幸的是,单行之道的原则一直是Python的渴望,而不是真正的指导原则。考虑一下Python中非常强大的数组理解。它们在功能上等效于map
和filter
函数:
[e for e in some_array if some_condition(e)]
filter(some_array, some_condition)
lambda
和 def
一样。
这是一个见解,但是我想说,Python语言中用于一般用途的任何东西如果没有明显破坏任何东西,都足够“ Pythonic”。
回答 5
更可取的是: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和功能:
- 可以作为参数传递(与常规函数相同)
- 在外部函数中创建时,将成为该外部函数的局部变量的闭包
但是,默认情况下,lambda缺少某些功能,这些功能是通过完整的函数定义语法获得的。
兰巴舞__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
鉴于我们没有相同的机制为lambda提供相同的信息:
>>> 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时,我也倾向于使用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
现在:
>>> 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对于更正式的情况是不利的,在这种情况下,您需要为以后将要出现的代码编辑者提供清晰的信息,特别是在它们不平凡的情况下。
我们知道应该给我们的对象起好名字。当对象没有物体时我们该怎么做名称,?
由于所有这些原因,我通常更喜欢使用def
代替with 来创建函数lambda
。
回答 6
我同意nosklo的建议:如果需要给函数命名,请使用def
。lambda
当我只是将简短的代码片段传递给另一个函数时,我会保留函数,例如:
a = [ (1,2), (3,4), (5,6) ]
b = map( lambda x: x[0]+x[1], a )
回答 7
在同意其他答案的同时,有时它更具可读性。这是一个lambda
方便使用的示例,在用例中,我经常遇到N维defaultdict
。
这是一个例子:
from collections import defaultdict
d = defaultdict(lambda: defaultdict(list))
d['Foo']['Bar'].append(something)
我发现它比def
为第二维创建更具可读性。对于更大的尺寸,这一点更为重要。
回答 8
lambda的主要用途一直是用于简单的回调函数,以及用于map,reduce,filter,后者需要将函数用作参数。随着列表理解成为规范,并且允许添加,如:
x = [f for f in range(1, 40) if f % 2]
很难想象在日常使用中使用lambda的真实情况。因此,我要避免使用lambda并创建嵌套函数。
回答 9
Lambda的一个重要限制是它们除了表达式外不能包含其他任何内容。一个lambda表达式几乎不可能产生除琐碎的副作用之外的其他任何东西,因为它的身体不能像人体一样富裕。def
“ ed函数”。
话虽这么说,Lua影响了我的编程风格,使之广泛地使用了匿名函数,并且我在其中充斥了代码。最重要的是,我倾向于以不考虑列表推导或生成器的方式将map / reduce视为抽象运算符,就像我要通过使用这些运算符明确推迟实现决策一样。
编辑:这是一个很老的问题,我对此事的看法有所改变。
首先,我强烈反对将lambda
表达式分配给变量。因为python具有专门的语法(提示,def
)。除此之外,lambda的许多用途(即使没有名称)也具有预定义的(且效率更高)的实现。例如,所讨论的示例可以缩写为just (1).__add__
,而无需将其包装在a lambda
或中def
。许多其他常见的用途可以使用的某种组合来满足operator
,itertools
和functools
模块。
回答 10
- 计算时间。
- 没有名称的功能。
- 实现一个功能和多个使用功能。
考虑一个简单的例子,
# CREATE ONE FUNCTION AND USE IT TO PERFORM MANY OPERATIONS ON SAME TYPE OF DATA STRUCTURE.
def variousUse(a,b=lambda x:x[0]):
return [b(i) for i in a]
dummyList = [(0,1,2,3),(4,5,6,7),(78,45,23,43)]
variousUse(dummyList) # extract first element
variousUse(dummyList,lambda x:[x[0],x[2],x[3]]) # extract specific indexed element
variousUse(dummyList,lambda x:x[0]+x[2]) # add specific elements
variousUse(dummyList,lambda x:x[0]*x[2]) # multiply specific elements
回答 11
如果仅要将lambda分配给本地范围内的变量,则最好使用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)
回答 12
我发现的lambda的一种用途是在调试消息中。
由于可以懒惰地评估lambda,因此您可以使用以下代码:
log.debug(lambda: "this is my message: %r" % (some_data,))
而不是可能很昂贵:
log.debug("this is my message: %r" % (some_data,))
即使调试调用由于当前的日志记录级别而没有产生输出,该命令也将处理格式字符串。
当然,要使它按所描述的那样工作,正在使用的日志记录模块必须支持lambda作为“惰性参数”(就像我的日志记录模块一样)。
相同的想法可以应用于按需内容值创建的任何其他惰性评估情况。
例如,此自定义三元运算符:
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)
如果使用lambda,则只会评估由条件选择的表达式,而不会评估lambda。
当然,您可以简单地使用函数而不是lambda,但是对于短表达式而言,lambda更精简。
回答 13
我同意nosklo。顺便说一句即使使用一次也扔掉功能,大多数情况下,您只想使用操作员模块中的某些功能。
EG:
您有一个带有此签名的函数:myFunction(data,callback function)。
您想传递一个添加2个元素的函数。
使用lambda:
myFunction(data, (lambda x, y : x + y))
pythonic方式:
import operator
myFunction(data, operator.add)
或当然,这是一个简单的示例,但是操作员模块提供了很多东西,包括用于列表和字典的项目设置器/获取器。真的很酷。
回答 14
一个主要的区别是您不能def
内联使用函数,我认为这是函数最方便的用例lambda
。例如,在对对象列表进行排序时:
my_list.sort(key=lambda o: o.x)
因此,我建议继续使用lambda进行此类琐碎的操作,这些操作也并不能真正受益于功能命名所提供的自动文档。
回答 15
lambda对于生成新函数很有用:
>>> def somefunc(x): return lambda y: x+y
>>> f = somefunc(10)
>>> f(2)
12
>>> f(4)
14