2013-12-12 46 views
1

在下面的代码中,使用列表理解还是生成器会更好?生成器vs列表理解

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) 

在这些测试中,如果列表很大发电机更快,如果列表是短名单理解显然更快。
这是因为理解是电脑只有一次?
对于大的列表:generator快于listcomp
对于小列表:generatorlistcomp

回答

5

in针对发电机表达将利用__iter__()方法的和,直到找到一个匹配迭代的表达,使之更有效地在一般情况下比列表理解,它在扫描结果进行匹配之前首先产生整个列表。

您的具体示例的替代方法是使用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 

在所有情况下,发电机的变体是远远快;你要缩短“短”版本只是元组的列表解析开始赢:

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

我会使用for循环,以便它不会构建整个列表,但它看起来很难看 – titus

+1

您可以在发生器表达式中使用'in'。它只是消耗发电机到第一场比赛。 –

+0

@gnibbler:ick,所以你可以; 'in'将会使用'__iter __()'来迭代,直到匹配。 –

0

创建发电机比创建一个列表慢,所以你必须要考虑到的变量:时间用于创建对象和时间检验的表达。所以要回答你的问题,如果“更好”,你的意思是“更快”:这取决于n

0

在创建生成器表达式时存在一定的开销,但最终不需要分配大量内存来弥补它。

小列表解析速度较快,因为它们没有那个开销。

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

到一个网络服务器中可能有100的或1000的连接,同时节省内存,这是特别重要的。