[英]Why this list comprehension is faster than equivalent generator expression?
我在Windows上使用Python 3.3.1 64位和此代码片段:
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 从列表理解到生成器表达式 :
... Python 3中的列表推导和生成器表达式实际上比它们在Python 2中更快! (并且两者之间不再存在速度差异。)
编辑:
我用timeit
测量了时间。 我知道它不是很准确,但我只关心这里的相对速度,当我用不同的迭代次数测试时,我的列表理解版本的时间总是缩短。
我相信这里的差异完全在于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快; 它们都需要大约相同的时间。 但是在list
上调用len
是即时的,而将1M数字相加则会使总时间再增加7%。
它扔了几个不同的号码,这似乎托起除非列出的是非常小(在这种情况下, 它似乎得到更快),或足够大的内存分配开始成为一个显著因子(它尚未,在333K)。
借用这个答案 ,有两件事需要考虑:
1. Python列表是可索引的,并且获取其长度仅需要O(1)次。 这意味着在列表上调用len()
的速度不依赖于它的大小。 但是,如果在生成器上调用len()
,则会消耗它生成的所有项,因此时间复杂度为O(n)。
2.参见上面的链接答案。 列表推导是一个紧凑的C循环,而生成器必须存储对迭代器内部的引用,并为它生成的每个项调用next(iter)
。 这为生成器创建了另一层开销。 在较小的范围内,可以安全地忽略列表推导和生成器之间的性能差异,但是在更大的范围内,您必须考虑这一点。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.