不太常见的情况:但是,如果您已经定义了一个函数,那么使用map通常是合理的,尽管它被认为是“不合理的”。例如,map(sum, myLists)比[sum(x) for x in myLists]更优雅/简洁。您获得了不必构造虚拟变量(例如sum(x) for x...或sum(_) for _...或sum(readableName) for readableName...)的优雅,您只需反复输入两次即可。对于filter和reduce以及itertools模块中的任何内容,同样的参数都适用:如果您已经有一个方便的函数,那么可以继续进行一些函数编程。这在某些情况下获得可读性,而在其他情况下(例如新手程序员、多个参数)则失去可读性。。。但是代码的可读性在很大程度上取决于您的注释。
>>> (str(n) for n in range(10**100))
<generator object <genexpr> at 0xacbdef>
基本上可以将[...]语法看作是将生成器表达式传递给列表构造函数,如list(x for x in range(5))。
简单的人工示例
from operator import neg
print({x:x**2 for x in map(neg,range(5))})
print({x:x**2 for x in [-y for y in range(5)]})
print({x:x**2 for x in (-y for y in range(5))})
列表理解是非惰性的,因此可能需要更多的内存(除非您使用generator comp恢复)。方括号[...]经常使事情变得明显,尤其是在一堆括号中。另一方面,有时你会像输入[x for x in...一样冗长。只要您保持迭代器变量简短,如果您不缩进代码,列表理解通常会更清晰。但是你可以一直缩进你的代码。
print(
{x:x**2 for x in (-y for y in range(5))}
)
或者分手:
rangeNeg5 = (-y for y in range(5))
print(
{x:x**2 for x in rangeNeg5}
)
Python3的效率比较
map现在很懒:
% python3 -mtimeit -s 'xs=range(1000)' 'f=lambda x:x' 'z=map(f,xs)'
1000000 loops, best of 3: 0.336 usec per loop ^^^^^^^^^
% python3 -mtimeit -s 'xs=range(1000)' 'f=lambda x:x' 'z=list(map(f,xs))'
10000 loops, best of 3: 165/124/135 usec per loop ^^^^^^^^^^^^^^^
for list(<map object>)
% python3 -mtimeit -s 'xs=range(1000)' 'f=lambda x:x' 'z=[f(x) for x in xs]'
10000 loops, best of 3: 181/118/123 usec per loop ^^^^^^^^^^^^^^^^^^
for list(<generator>), probably optimized
% python3 -mtimeit -s 'xs=range(1000)' 'f=lambda x:x' 'z=list(f(x) for x in xs)'
1000 loops, best of 3: 215/150/150 usec per loop ^^^^^^^^^^^^^^^^^^^^^^
for list(<generator>)
结果以A A A/BBB/CCC的形式显示,其中A是在使用python 3的circa-2010英特尔工作站上执行的。?。?,B和C是在一个使用python 3.2.1的circa-2013 AMD工作站上执行的,硬件非常不同。结果表明,映射和列表理解在性能上具有可比性,且受其他随机因素的影响最大。奇怪的是,虽然我们希望列表理解[...]比生成器表达式(...)执行得更好,但是map也比生成器表达式更有效(再次假设所有值都是求值/使用的)。
$ python -mtimeit -s'xs=range(10)' 'map(hex, xs)'
100000 loops, best of 3: 4.86 usec per loop
$ python -mtimeit -s'xs=range(10)' '[hex(x) for x in xs]'
100000 loops, best of 3: 5.58 usec per loop
当map需要lambda时,如何完全反转性能比较的示例:
$ python -mtimeit -s'xs=range(10)' 'map(lambda x: x+2, xs)'
100000 loops, best of 3: 4.24 usec per loop
$ python -mtimeit -s'xs=range(10)' '[x+2 for x in xs]'
100000 loops, best of 3: 2.32 usec per loop
Python 2:应该使用
map
和filter
而不是列表理解一个目标即使他们不是“Python”你也应该喜欢他们的原因是:
它们需要函数/lambdas作为参数,这会引入一个新的作用域。
我不止一次被这个咬过:
但如果我说:
那一切都会好起来的。
你可以说我在同一个范围内使用相同的变量名是愚蠢的。
我没有。代码本来很好——两个
x
不在同一范围内。直到我将内部块移动到代码的不同部分之后,才出现了问题(读:维护期间的问题,而不是开发期间的问题),我没有预料到。
是的,如果你从未犯过这个错误,那么列表理解会更优雅。
但从个人经验(以及看到其他人犯同样的错误)来看,我已经看到它发生了足够多的次数,我认为当这些错误潜入到代码中时,不值得你付出痛苦。
结论:
使用
map
和filter
。它们可以防止与范围相关的细微的难以诊断的错误。旁注:
如果适合你的情况,不要忘记考虑使用
imap
和ifilter
(initertools
)!案例
map
通常是合理的,尽管它被认为是“不合理的”。例如,map(sum, myLists)
比[sum(x) for x in myLists]
更优雅/简洁。您获得了不必构造虚拟变量(例如sum(x) for x...
或sum(_) for _...
或sum(readableName) for readableName...
)的优雅,您只需反复输入两次即可。对于filter
和reduce
以及itertools
模块中的任何内容,同样的参数都适用:如果您已经有一个方便的函数,那么可以继续进行一些函数编程。这在某些情况下获得可读性,而在其他情况下(例如新手程序员、多个参数)则失去可读性。。。但是代码的可读性在很大程度上取决于您的注释。map
函数作为纯抽象函数,在这里您将map
映射或使用map
,或者从将map
作为函数讨论中获益。例如,在Haskell中,一个名为fmap
的函子接口泛化了任何数据结构上的映射。这在python中是非常少见的,因为python语法迫使您使用生成器样式来讨论迭代;您不能很容易地将其泛化。(这有时是好的,有时是坏的)您可能会想出一些罕见的python示例,其中map(f, *lists)
是一个合理的做法。我能想到的最接近的例子是sumEach = partial(map,sum)
,这是一个单行程序,它大致相当于:for
循环:当然也可以只使用for循环。虽然从函数式编程的角度来看,非局部变量并没有那么优雅,但有时在命令式编程语言(如python)中,非局部变量会使代码更加清晰,因为人们非常习惯这样阅读代码。For循环通常也是最有效的,当你只是做任何复杂的操作,而不是建立一个类似列表的理解和地图优化(例如,求和,或建立一棵树,等等),至少在内存方面是有效的(不一定是时间方面,我最坏的期望是一个常数因子,除非有一些罕见的病理性垃圾收集打嗝)。“Python病”
我不喜欢“pythonic”这个词,因为我觉得pythonic在我眼里并不总是优雅的。然而,
map
和filter
以及类似的函数(比如非常有用的itertools
模块)在风格上可能被认为是不通顺的。懒惰
就效率而言,与大多数函数式编程结构一样,MAP可以是LAZY,实际上在python中是LAZY。这意味着您可以这样做(在python3中),并且您的计算机不会耗尽内存并丢失所有未保存的数据:
试着用一个清单来理解:
请注意,列表理解本身也是惰性的,但是python选择将它们实现为非惰性的。不过,python确实支持以生成器表达式的形式进行惰性列表理解,如下所示:
基本上可以将
[...]
语法看作是将生成器表达式传递给列表构造函数,如list(x for x in range(5))
。简单的人工示例
列表理解是非惰性的,因此可能需要更多的内存(除非您使用generator comp恢复)。方括号
[...]
经常使事情变得明显,尤其是在一堆括号中。另一方面,有时你会像输入[x for x in...
一样冗长。只要您保持迭代器变量简短,如果您不缩进代码,列表理解通常会更清晰。但是你可以一直缩进你的代码。或者分手:
Python3的效率比较
map
现在很懒:因此,如果您不使用所有数据,或者不提前知道需要多少数据,python3中的
map
(python2或python3中的生成器表达式)将避免计算它们的值,直到最后一刻。通常,这将超过使用map
的任何开销。缺点是,与大多数函数式语言相比,这在python中非常有限:只有从左到右“按顺序”访问数据时,才能获得此好处,因为python生成器表达式只能按x[0], x[1], x[2], ...
的顺序计算。然而,假设我们有一个预先生成的函数
f
,我们希望map
,并且通过立即使用list(...)
强制求值来忽略map
的惰性。我们得到了一些非常有趣的结果:结果以A A A/BBB/CCC的形式显示,其中A是在使用python 3的circa-2010英特尔工作站上执行的。?。?,B和C是在一个使用python 3.2.1的circa-2013 AMD工作站上执行的,硬件非常不同。结果表明,映射和列表理解在性能上具有可比性,且受其他随机因素的影响最大。奇怪的是,虽然我们希望列表理解
[...]
比生成器表达式(...)
执行得更好,但是map
也比生成器表达式更有效(再次假设所有值都是求值/使用的)。重要的是要认识到这些测试假设了一个非常简单的函数(identity函数);但是这很好,因为如果函数很复杂,那么与程序中的其他因素相比,性能开销可以忽略不计。(用
f=lambda x:x+x
这样的简单方法进行测试可能还是很有趣的)如果您擅长阅读python程序集,那么可以使用
dis
模块来查看这是否是幕后发生的事情:似乎使用
[...]
语法比list(...)
更好。遗憾的是,map
类对于反汇编来说有点不透明,但是我们可以通过速度测试来完成。map
在某些情况下(当您不是为了这个目的而制作lambda,而是在map和listcomp中使用相同的函数时)可能在显微镜下更快。在其他情况下,列表理解可能更快,大多数(并非所有)Python认为它们更直接、更清晰。使用完全相同的功能时,map的微小速度优势示例:
当map需要lambda时,如何完全反转性能比较的示例:
相关问题 更多 >
编程相关推荐