编辑 :由于没有提供更多的解释/示例,我被注释中的任务(部分地,似乎是由FP的狂热者(但不是排他性的))提供的,因此扩大了答案以提供一些解释/示例。
lambda,更是这样
map(和
filter), 而
最特别的是
reduce,在几乎没有在Python中,这是一个强烈的多范式语言工作的工具。
lambda与普通
def语句相比,主要优点(?)是它可以创建一个 匿名 函数,同时
def为该函数提供一个名称-
而对于这一非常可疑的优点,您将付出巨大的代价(该函数的主体仅限于一个表达式,即结果函数对象不是可腌制的,缺少名称有时会使理解堆栈跟踪或以其他方式调试问题变得更加困难-
我需要继续吗?!-)。
考虑一下您有时会在“ Python”中使用的最愚蠢的惯用法(带有“吓人的引号”的Python,因为它显然 不是 惯用的Python-
从惯用Scheme之类的音译中很糟糕,就像更频繁地过度使用) Python中的OOP是Java或类似语言的错误音译):
inc = lambda x: x + 1
通过为lambda分配名称,此方法将立即丢弃上述“优点”,并且不会丢失任何缺点!例如,
inc不 知道
它的名字-
inc.__name__是无用的字符串
'<lambda>'-祝您好运,了解其中的一些堆栈跟踪;-)。在这种简单情况下,实现所需语义的正确Python方法当然是:
def inc(x): return x + 1
现在
inc.__name__是字符串
'inc',这显然是应该的,并且该对象是可腌制的-
语义是相同的(在这种简单的情况下,所需的功能可以轻松地用一个简单的表达式表示)-
def如果需要,它也可以很容易地进行重构暂时或永久插入语句,例如
raise)。
lambda是一个表达式的
def一部分,而是一个语句的一部分-
这是使人们
lambda有时使用的语法糖的一点点。许多FP爱好者(与许多OOP和程序迷一样)不喜欢Python在表达式和语句之间的合理区分(这是对Command-
Query Separation的一般立场的一部分)。我,我认为,当您使用一种语言时,最好不要“杂乱无章”地使用它- 设计 要使用的方式-
而不是与之抗争。因此我以Python方式编程Python,以示意图(;-)方式编程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
…我们 几乎
可以回到最简单,最明显,紧凑和易读的方法(简单,基本,琐碎的循环)容易获得的性能。这
lambda实际上指出了另一个问题:性能!对于足够简单的操作(例如乘法),与实际执行的操作相比,函数调用的开销相当大-
reduce(and
map和
filter)通常会迫使您在简单循环,列表推导和生成器表达式中插入此类函数调用允许在线操作的可读性,紧凑性和速度。
也许比上述“为名称分配lambda”反习惯用语更糟,实际上它是以下反习惯用语,例如,按字符串的长度对列表进行排序:
thelist.sort(key=lambda s: len(s))
而不是明显的,可读的,紧凑的,更快的
thelist.sort(key=len)
在这里,使用
lambda只是插入了一定程度的间接操作-什么都没有产生任何效果,也有很多不好的效果。
使用的动机
lambda通常是允许使用
map和
filter而不是使用更可取的循环或列表理解,而不是让您在线进行普通的常规计算。当然,您仍然需要支付“间接级别”。不必怀疑“我应该在这里使用listcomp还是地图”,这并不是Python的意思:只是始终使用listcomps,当两者都适用且您不知道选择哪个时,基于“应该有一个,并且最好只有一种明显的做事方式”。您经常会编写无法明智地转换为映射(嵌套循环,
if子句等)的listcomp,而没有对它的调用
map不能明智地重写为listcomp。
在Python中,完全正确的函数方法通常包括列表理解,生成器表达式,
itertools高阶函数,各种伪装的一阶函数,闭包,生成器(有时还包括其他种类的迭代器)。
itertools作为评论者指出的那样,不包括
imap和
ifilter:所不同的是,像所有itertools,这些是基于流的(像
map和
filter在Python
3建宏,但不同于那些内建在Python 2)。
itertools提供了一组相互融合且性能出色的构建块:尤其是如果您发现自己可能处理很长(甚至是无穷大!)的序列,则应归功于自己熟悉itertools-
它们的整体文档中的章节有助于您阅读,尤其是食谱很有启发性。
编写自己的高阶函数通常很有用,尤其是当它们适合用作装饰器时(如文档那部分所述的函数装饰器和Python
2.6中引入的类装饰器)。切记在函数装饰器上使用functools.wraps(以保持包裹函数的元数据)!
因此,总结一下……:可以使用
lambda,
map和进行编码的任何事情
filter,都可以使用
def(命名函数)和listcomps进行编码(通常不是很方便),并且通常将一个缺口移到生成器,生成器表达式或上
itertools。更好。
reduce符合“有吸引力的滋扰”的法律定义…: 几乎从来都不是 适合该工作的工具(这就是为什么它最终不再是Python
3中的内置工具!)。



