为什么这个列表理解比等效的生成器表达更快?

时间:2013-04-30 19:15:00

标签: python python-3.x list-comprehension generator-expression

我在Windows上使用64位64位Python和此代码段:

len ([None for n in range (1, 1000000) if n%3 == 1])
与此相比,

执行时间为136毫秒:

sum (1 for n in range (1, 1000000) if n%3 == 1)

在146ms内执行。在这种情况下,生成器表达式不应该比列表理解更快或更快吗?

我引自Guido van Rossum From List Comprehensions to Generator Expressions

  

... Python 3中的列表推导和生成器表达式都是   实际上比他们在Python 2中更快! (并且不再有了   两者之间的速度差异。)

编辑:

我用timeit测量了时间。我知道它不是很准确,但是我只关心这里的相对速度,当我用不同的迭代次数进行测试时,我的列表理解版本的时间总是缩短。

2 个答案:

答案 0 :(得分:6)

我认为这里的差异完全取决于1000000的成本增加。在Mac OS X上使用64位Python.org 3.3.0进行测试:

In [698]: %timeit len ([None for n in range (1, 1000000) if n%3 == 1])
10 loops, best of 3: 127 ms per loop
In [699]: %timeit sum (1 for n in range (1, 1000000) if n%3 == 1)
10 loops, best of 3: 138 ms per loop
In [700]: %timeit sum ([1 for n in range (1, 1000000) if n%3 == 1])
10 loops, best of 3: 139 ms per loop

所以,理解并不比genexp快;它们都需要大约相同的时间。但是在len上调用list是即时的,而将1M数字相加会使总时间再增加7%。

在它上面抛出几个不同的数字,除非列表非常小(在这种情况下 似乎变得更快),或者足够大以至于内存分配开始变得非常小,这似乎有用了一个重要的因素(尚未达到333K)。

答案 1 :(得分:0)

this answer借来的,有两件事要考虑:

1。。Python列表是可索引的,并且获取其长度仅需O(1)次。这意味着在列表上调用len()的速度并不取决于其大小。但是,如果在生成器上调用len(),则会消耗掉它生成的所有项,因此时间复杂度为O(n)。

2。。请参见上面的链接答案。列表理解是一个紧密的C循环,而生成器必须在内部存储对迭代器的引用,并为其生成的每个项目调用next(iter)。这为发电机增加了另一层开销。在较小的规模上,列表理解和生成器之间的性能差异可以安全地忽略,但是在较大的规模上,您必须考虑这一点。