发电机与列表理解

时间:2013-12-12 11:55:47

标签: python generator list-comprehension

在下面的代码中,最好使用列表推导还是生成器?

from itertools import izip
n=2
l=izip(xrange(10**n), xrange(10**n))
print 3 not in [x[0] for x in l]
#or
#print 3 not in (x[0] for x in l)

在这些测试中,如果列表很大,则生成器更快,如果列表更短,列表理解显然更快。
这是因为理解只是计算机一次吗? 对于大型列表:generatorlistcomp更快 对于小型列表:generator慢于listcomp

3 个答案:

答案 0 :(得分:5)

针对生成器表达式的

in将使用__iter__()方法并迭代表达式直到找到匹配项,从而使其在一般情况下更高效 list comprehension,在扫描结果进行匹配之前首先生成整个列表。

您的具体示例的替代方法是使用any(),以使测试更加明确。我发现这有点可读:

any(x[0] == 3 for x in l)

您必须考虑in转发生成器;如果您还需要在其他地方使用生成器,则无法使用此方法。

至于你的具体时间测试;你的“短期”测试存在致命缺陷。 izip()生成器的第一次迭代将完全耗尽,使其他9999次迭代针对生成器进行测试。您正在测试创建空列表和空生成器之间的区别,放大创建成本差异。

此外,您应该使用timeit module来运行测试,确保测试可重复。这意味着您每次迭代都必须创建一个新的izip()对象;现在对比度更大

>>> # Python 2, 'short'
...
>>> timeit.timeit("l = izip(xrange(10**2), xrange(10**2)); 3 not in (x[0] for x in l)", 'from itertools import izip', number=100000)
0.27606701850891113
>>> timeit.timeit("l = izip(xrange(10**2), xrange(10**2)); 3 not in [x[0] for x in l]", 'from itertools import izip', number=100000)
1.7422130107879639
>>> # Python 2, 'long'
...
>>> timeit.timeit("l = izip(xrange(10**3), xrange(10**3)); 3 not in (x[0] for x in l)", 'from itertools import izip', number=100000)
0.3002200126647949
>>> timeit.timeit("l = izip(xrange(10**3), xrange(10**3)); 3 not in [x[0] for x in l]", 'from itertools import izip', number=100000)
15.624258995056152

并在Python 3上:

>>> # Python 3, 'short'
... 
>>> timeit.timeit("l = zip(range(10**2), range(10**2)); 3 not in (x[0] for x in l)", number=100000)
0.2624585109297186
>>> timeit.timeit("l = zip(range(10**2), range(10**2)); 3 not in [x[0] for x in l]", number=100000)
1.5555254180217162
>>> # Python 3, 'long'
... 
>>> timeit.timeit("l = zip(range(10**3), range(10**3)); 3 not in (x[0] for x in l)", number=100000)
0.27222433499991894
>>> timeit.timeit("l = zip(range(10**3), range(10**3)); 3 not in [x[0] for x in l]", number=100000)
15.76974998600781

在所有情况下,发电机型号的速度要快得多;你必须将'短'版本缩短为 8 元组才能使列表理解开始获胜:

>>> timeit.timeit("n = 8; l = izip(xrange(n), xrange(n)); 3 not in (x[0] for x in l)", 'from itertools import izip', number=100000)
0.2870941162109375
>>> timeit.timeit("n = 8; l = izip(xrange(n), xrange(n)); 3 not in [x[0] for x in l]", 'from itertools import izip', number=100000)
0.28503894805908203

在Python 3上,生成器表达式和列表推导的实现更接近,你必须在列表理解获胜之前归结为4个项目:

>>> timeit.timeit("n = 4; l = zip(range(n), range(8)); 3 not in (x[0] for x in l)", number=100000)
0.284480107948184
>>> timeit.timeit("n = 4; l = zip(range(n), range(8)); 3 not in [x[0] for x in l]", number=100000)
0.23570425796788186

答案 1 :(得分:0)

创建生成器比创建列表要慢,因此您必须考虑变量:创建对象的时间和测试表达式的时间。所以要回答你的问题,如果“更好”,你的意思是“更快”:它取决于n

答案 2 :(得分:0)

创建生成器表达式会产生相当大的开销,但最终您无需分配大量内存即可弥补它。

小列表理解速度更快,因为它们没有这种开销。

通常小的情况足够接近,所以在这种情况下最好选择一个生成器表达式

在可能同时存在100个或1000个连接的网络服务器上节省内存尤为重要。