Edit:我在评论中因为没有提供更多的解释/示例而受到批评(看起来部分是由 Python 中的 FP 狂热者引起的,但不完全是),因此,扩展答案以提供一些。
lambda
, 更是如此map
(and filter
), and最特别的是reduce
,几乎都不是 Python 中这项工作的正确工具,Python 是一种强大的多范式语言。
lambda
与普通相比的主要优点(?)def
声明是它使匿名的函数,同时def
给函数一个名字——为了这个非常可疑的优势,你付出了巨大的代价(函数的主体仅限于一个表达式,生成的函数对象不可pickle,缺乏名称有时会使理解一个函数变得更加困难)堆栈跟踪或以其他方式调试问题 - 我需要继续吗?!-)。
考虑一下您有时在“Python”中看到的最愚蠢的习惯用法是什么(Python 带有“恐吓引号”,因为它显然是not惯用的Python——这是从惯用的Scheme或类似语言的一个糟糕的音译,就像Python中更频繁地过度使用OOP是从Java或类似的语言的一个糟糕的音译一样):
inc = lambda x: x + 1
通过为 lambda 分配一个名称,这种方法立即抛弃了上述“优点”——并且不会失去任何缺点!例如,inc
不know其名称 -inc.__name__
是无用的字符串'<lambda>'
-- 祝你好运,了解其中的一些堆栈跟踪;-)。当然,在这个简单的例子中实现所需语义的正确 Python 方法是:
def inc(x): return x + 1
Now inc.__name__
是字符串'inc'
,显然应该如此,并且该对象是可腌制的 - 语义在其他方面是相同的(在这个简单的情况下,所需的功能可以轻松地适合一个简单的表达式 -def
如果您需要临时或永久插入诸如以下之类的语句,那么重构也变得非常容易print
or raise
, 当然)。
lambda
是一个表达式(的一部分),而def
是一个语句(的一部分)——这是让人们使用的一点语法糖lambda
有时。许多 FP 爱好者(就像许多 OOP 和过程式爱好者一样)不喜欢 Python 在表达式和语句之间相当强烈的区别(这是对 Python 的总体立场的一部分)命令查询分离 http://en.wikipedia.org/wiki/Command-query_separation)。我,我认为当你使用一种语言时,你最好“顺其自然”地使用它——就像它本来的样子designed被利用——而不是与之对抗;所以我用Pythonic方式编程Python,用Schematic (;-)方式编程Scheme,用Fortesque (?)方式编程Fortran,等等:-)。
继续到reduce
——一条评论声称reduce
是计算列表乘积的最佳方法。哦真的吗?让我们来看看...:
$ python -mtimeit -s'L=range(12,52)' 'reduce(lambda x,y: x*y, L, 1)'
100000 loops, best of 3: 18.3 usec per loop
$ python -mtimeit -s'L=range(12,52)' 'p=1' 'for x in L: p*=x'
100000 loops, best of 3: 10.5 usec per loop
因此,简单、基本、琐碎的循环大约是执行任务的“最佳方式”的两倍(并且更简洁)?-)我想速度和简洁性的优势必须使琐碎循环成为“最好的方法” “这样吧,对吧?-)
通过进一步牺牲紧凑性和可读性......:
$ python -mtimeit -s'import operator; L=range(12,52)' 'reduce(operator.mul, L, 1)'
100000 loops, best of 3: 10.7 usec per loop
...我们可以得到almost回到最简单、最明显、紧凑且可读的方法(简单、基本、琐碎的循环)轻松获得的性能。这指出了另一个问题lambda
,其实:性能!对于足够简单的操作(例如乘法),与正在执行的实际操作相比,函数调用的开销相当大 -reduce
(and map
and filter
)通常会迫使您在简单循环、列表推导式和生成器表达式中插入这样的函数调用,以实现内联操作的可读性、紧凑性和速度。
也许比上面斥责的“将 lambda 分配给名称”反惯用法更糟糕的是实际上以下反惯用法,例如按长度对字符串列表进行排序:
thelist.sort(key=lambda s: len(s))
而不是显而易见的、可读的、紧凑的、更快的
thelist.sort(key=len)
在这里,使用lambda
除了插入一定程度的间接性之外什么也没做——没有任何好的效果,而且还有很多不好的效果。
使用动机lambda
通常是允许使用map
and filter
而不是更可取的循环或列表理解,可以让您在线进行简单、正常的计算;当然,您仍然需要支付“间接级别”的费用。必须想知道“我应该在这里使用列表计算还是地图”并不是Pythonic:只要总是使用列表计算,当两者都适用并且您不知道选择哪一个时,基于“应该有一个,并且最好只有一种、明显的方式来做某事”。您经常会编写无法明智地转换为映射的列表计算(嵌套循环,if
条款等),而没有调用map
不能明智地将其重写为 listcomp。
Python 中完美的函数式方法通常包括列表推导式、生成器表达式、itertools
、高阶函数、各种形式的一阶函数、闭包、生成器(有时还有其他类型的迭代器)。
itertools
正如评论者指出的那样,确实包括imap
and ifilter
:区别在于,像所有 itertools 一样,它们是基于流的(例如map
and filter
Python 3 中的内置函数,但与 Python 2 中的内置函数不同)。itertools
提供了一组可以很好地组合在一起的构建块以及出色的性能:特别是如果您发现自己可能正在处理很长(甚至无限!-)的序列,那么您应该熟悉 itertools - 它们的整体chapter http://docs.python.org/library/itertools.html文档中的内容有助于良好的阅读,并且recipes http://docs.python.org/library/itertools.html?highlight=itertools#recipes特别是很有启发性。
编写自己的高阶函数通常很有用,特别是当它们适合用作装饰者 http://docs.python.org/reference/compound_stmts.html#function(函数装饰器,如文档该部分中所解释的,以及类装饰器,在 Python 2.6 中引入)。一定要记得使用functools.wraps http://docs.python.org/library/functools.html?highlight=decorators#functools.wraps在你的函数装饰器上(以保持函数的元数据被包装)!
所以,总结一下……:任何你可以用来编码的东西lambda
, map
, and filter
,您可以使用以下代码(通常是有利的)def
(命名函数)和列表计算——通常会上升一级到生成器、生成器表达式或itertools
,甚至更好。reduce
符合“有吸引力的滋扰”的法律定义......:它是几乎没有适合这项工作的工具(这就是为什么它终于不再是 Python 3 中的内置工具了!-)。