2013-12-12 51 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的連接,同時節省內存,這是特別重要的。