Python 2 vs 3:用map()替换列表推导?

时间:2014-09-15 10:25:18

标签: python performance python-3.x list-comprehension map-function

鉴于以下测试:

>>> import timeit
>>> timeit.timeit("[x + 'abc' for x in ['x', 'y', 'z']]")
>>> timeit.timeit("map(lambda x: x + 'abc', ['x', 'y', 'z'])")

使用Python 2.7和3.4(Debian 8 / testing / jessie),我得到以下数字:

Python27 Python34
1.3s     0.5s      map()
0.6s     0.9s      list comprehension

使用Python 3显着改善了地图,列表理解受到了严重影响。

问题:将代码从Python 2移植到Python 3时,是否建议将列表推导更改为map()?

1 个答案:

答案 0 :(得分:6)

您没有正确测试。在Python 3中,map()返回迭代器,而不是列表。您实际上并没有在测试中进行迭代,只测试迭代器的创建。

您需要包含迭代以查看哪种方法更快;你可以使用长度为0的collections.deque(),这将迭代而不产生新的列表对象:

import timeit
timeit.timeit("deque([x + 'abc' for x in ['x', 'y', 'z']], maxlen=0)",
              'from collections import deque')
timeit.timeit("deque(map(lambda x: x + 'abc', ['x', 'y', 'z']), maxlen=0)",
              'from collections import deque')

通过将deque()应用于两者,您甚至可以再次获得分数。

现在列表推导在两个平台上都获胜:

Python27 Python34
1.91s     2.00s      map()
1.18s     1.85s      list comprehension

你应该使用更大的输入列表来正确测试差异;太多了

在Python 3上放慢列表理解速度的原因是因为它们有自己的适当范围,就像生成器表达式和dict以及集合理解在Python 2和3上一样。

如果你的map函数完全用C实现(而不是lambda,它推回到Python,map() 可以获胜:

>>> timeit.timeit("deque([m(i) for i in ['x', 'y', 'z']], maxlen=0)",
...               "from collections import deque; from operator import methodcaller; m = methodcaller('__add__', 'abc')")
2.3514049489967874
>>> timeit.timeit("deque(map(methodcaller('__add__', 'abc'), ['x', 'y', 'z']), maxlen=0)",
...               'from collections import deque; from operator import methodcaller')
1.7684289459939464

这里methodcaller() object通过为每个使用的对象调用str.__add__方法来避免回调到Python代码。