[英]When is not a good time to use python generators?
這與您可以使用 Python 生成器函數做什么相反? :python 生成器、生成器表達式和itertools
模塊是我itertools
最喜歡的 Python 功能。 它們在設置操作鏈以對大量數據執行時特別有用——我在處理 DSV 文件時經常使用它們。
那么什么時候不適合使用生成器、生成器表達式或itertools
函數呢?
zip()
不是itertools.izip()
,或者range()
超過xrange()
,或[x for x in foo]
over (x for x in foo)
?顯然,我們最終需要將生成器“解析”為實際數據,通常是通過創建一個列表或使用非生成器循環對其進行迭代。 有時我們只需要知道長度。 這不是我要問的。
我們使用生成器,這樣我們就不會為臨時數據分配新列表到內存中。 這對於大型數據集尤其有意義。 它對小數據集也有意義嗎? 是否有明顯的內存/CPU 權衡?
鑒於對列表理解性能與 map() 和 filter()的令人大開眼界的討論,如果有人對此進行了一些分析,我特別感興趣。 ( 替代鏈接)
在以下情況下使用列表而不是生成器:
1)您需要訪問數據多次(即高速緩存的結果,而不是重新計算它們的):
for i in outer: # used once, okay to be a generator or return a list
for j in inner: # used multiple times, reusing a list is better
...
2)您需要隨機訪問(或除前向順序以外的任何訪問):
for i in reversed(data): ... # generators aren't reversible
s[i], s[j] = s[j], s[i] # generators aren't indexable
3)您需要加入字符串(這需要對數據進行兩次傳遞):
s = ''.join(data) # lists are faster than generators in this use case
4) 您使用的PyPy有時無法像使用普通函數調用和列表操作那樣優化生成器代碼。
一般來說,當你需要列表操作時不要使用生成器,比如 len()、reversed() 等等。
有時您也可能不想進行惰性求值(例如,預先進行所有計算以便釋放資源)。 在這種情況下,列表表達式可能更好。
簡介,簡介,簡介。
分析您的代碼是了解您所做的事情是否有任何影響的唯一方法。
xrange、生成器等的大多數用途都超過靜態大小、小數據集。 只有當您處理大型數據集時,它才會真正發揮作用。 range() 與 xrange() 主要只是讓代碼看起來更難看一點,並且不會丟失任何東西,並且可能會獲得一些東西。
簡介,簡介,簡介。
正如您提到的,“這對於大型數據集尤其有意義”,我認為這回答了您的問題。
如果您沒有碰到任何障礙,在性能方面,您仍然可以堅持使用列表和標准功能。 然后,當您遇到性能問題時,請進行切換。
然而,正如@u0b34a0f6ae 在評論中提到的,在開始時使用生成器可以讓您更輕松地擴展到更大的數據集。
關於性能:如果使用 psyco,列表可能比生成器快很多。 在下面的示例中,使用 psyco.full() 時,列表速度快了近 50%
import psyco
import time
import cStringIO
def time_func(func):
"""The amount of time it requires func to run"""
start = time.clock()
func()
return time.clock() - start
def fizzbuzz(num):
"""That algorithm we all know and love"""
if not num % 3 and not num % 5:
return "%d fizz buzz" % num
elif not num % 3:
return "%d fizz" % num
elif not num % 5:
return "%d buzz" % num
return None
def with_list(num):
"""Try getting fizzbuzz with a list comprehension and range"""
out = cStringIO.StringIO()
for fibby in [fizzbuzz(x) for x in range(1, num) if fizzbuzz(x)]:
print >> out, fibby
return out.getvalue()
def with_genx(num):
"""Try getting fizzbuzz with generator expression and xrange"""
out = cStringIO.StringIO()
for fibby in (fizzbuzz(x) for x in xrange(1, num) if fizzbuzz(x)):
print >> out, fibby
return out.getvalue()
def main():
"""
Test speed of generator expressions versus list comprehensions,
with and without psyco.
"""
#our variables
nums = [10000, 100000]
funcs = [with_list, with_genx]
# try without psyco 1st
print "without psyco"
for num in nums:
print " number:", num
for func in funcs:
print func.__name__, time_func(lambda : func(num)), "seconds"
print
# now with psyco
print "with psyco"
psyco.full()
for num in nums:
print " number:", num
for func in funcs:
print func.__name__, time_func(lambda : func(num)), "seconds"
print
if __name__ == "__main__":
main()
結果:
without psyco
number: 10000
with_list 0.0519102208309 seconds
with_genx 0.0535933367509 seconds
number: 100000
with_list 0.542204280744 seconds
with_genx 0.557837353115 seconds
with psyco
number: 10000
with_list 0.0286369007033 seconds
with_genx 0.0513424889137 seconds
number: 100000
with_list 0.335414877839 seconds
with_genx 0.580363490491 seconds
就性能而言,我想不出任何時候您會想要在生成器上使用列表。
我從未發現生成器會阻礙您嘗試做的事情的情況。 然而,在很多情況下,使用生成器與不使用它們一樣對您沒有幫助。
例如:
sorted(xrange(5))
不提供任何改進:
sorted(range(5))
如果您以后需要將值保留在其他地方並且您的集合的大小不太大,您應該更喜歡列表推導式。
例如:您正在創建一個列表,稍后您將在程序中多次循環該列表。
在某種程度上,您可以將生成器視為迭代(循環)與列表推導式的替代品,作為一種數據結構初始化。 如果要保留數據結構,請使用列表推導式。
生成器構建可枚舉的值列表。 當迭代過程可以按需使用值時,可枚舉非常有用。 構建你的生成器需要時間,所以如果列表大小為數百萬條記錄,使用sql server處理sql中的數據可能更有用。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.