[python] 生成器表達式與列表理解



3 Answers

遍歷生成器表達式列表理解將執行相同的操作。 然而, 列表理解將首先在內存中創建整個列表,而生成器表達式將動態創建項目,因此您可以將它用於非常大的(也是無限的!)序列。

Question

什麼時候應該使用生成器表達式,什麼時候應該在Python中使用列表推導?

# Generator expression
(x*2 for x in range(256))

# List comprehension
[x*2 for x in range(256)]



重要的一點是,列表理解會創建一個新列表。 生成器創建一個可迭代的對象,在您消耗位時即時“過濾”源材料。

假設您有一個名為“hugefile.txt”的2TB日誌文件,並且您希望所有以“ENTRY”開頭的行的內容和長度。

所以你試著寫一個列表理解開始:

logfile = open("hugefile.txt","r")
entry_lines = [(line,len(line)) for line in logfile if line.startswith("ENTRY")]

這會吸引整個文件,處理每一行,並將匹配的行存儲在數組中。 因此這個數組可以包含高達2TB的內容。 這是很多內存,可能不適合你的目的。

因此,我們可以使用生成器將“過濾器”應用於我們的內容。 除非我們開始迭代結果,否則實際上沒有數據被讀取。

logfile = open("hugefile.txt","r")
entry_lines = ((line,len(line)) for line in logfile if line.startswith("ENTRY"))

還沒有從我們的文件中讀取一行。 事實上,假設我們想要進一步篩選我們的結果:

long_entries = ((line,length) for (line,length) in entry_lines if length > 80)

還沒有人讀過,但我們現在已經指定了兩個生成器,它們將根據我們的意願來處理我們的數據。

讓我們寫出我們的過濾行到另一個文件:

outfile = open("filtered.txt","a")
for entry,length in long_entries:
    outfile.write(entry)

現在我們讀取輸入文件。 由於for循環繼續請求附加行, long_entries生成器需要來自entry_lines生成器的行,僅返回長度大於80個字符的行。 然後, entry_lines生成器從logfile迭代器請求行(如指示的那樣過濾),然後讀取該文件。

因此,不是以完全填充的列表的形式將數據“推送”到輸出函數,而是給輸出函數一種僅在需要時才“拉”數據的方式。 這在我們的案例中效率更高,但不夠靈活。 發電機是一種方式,一次通過; 我們讀取的日誌文件中的數據立即被丟棄,所以我們不能回到上一行。 另一方面,一旦我們完成了數據,我們不必擔心數據的存在。




有時你可以從itertools得到tee函數,它會為可以獨立使用的同一個生成器返回多個迭代器。




從可變對象(如列表)創建生成器時,請注意生成器將在使用生成器時在列表的狀態下進行評估,而不是在創建生成器時進行評估:

>>> mylist = ["a", "b", "c"]
>>> gen = (elem + "1" for elem in mylist)
>>> mylist.clear()
>>> for x in gen: print (x)
# nothing

如果您的列表有任何修改的機會(或者列表中的可變對象),但您需要創建生成器時的狀態,則需要使用列表理解。






Related