1

给定以下测试:

>>> 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

Map 在 Python 3 中得到了显着改进,列表理解受到了严重影响。

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

4

1 回答 1

6

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

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

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 上变慢的原因是因为它们有自己的适当范围,就像 Python 2 和 3 上的生成器表达式和 dict 和集合推导一样。

如果你的 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()对象通过调用str.__add__每个使用的对象的方法来避免回调 Python 代码。

于 2014-09-15T10:28:13.387 回答