天天看點

《編寫高品質Python代碼的59個有效方法》——第9條:用生成器表達式來改寫資料量較大的清單推導

本節書摘來自華章社群《編寫高品質python代碼的59個有效方法》一書中的第9條:用生成器表達式來改寫資料量較大的清單推導,作者[美]布雷特·斯拉特金(brett slatkin),更多章節内容可以通路雲栖社群“華章社群”公衆号檢視

第9條:用生成器表達式來改寫資料量較大的清單推導

清單推導(參見本書第7條)的缺點是:在推導過程中,對于輸入序列中的每個值來說,可能都要建立僅含一項元素的全新清單。當輸入的資料比較少時,不會出問題,但如果輸入的資料非常多,那麼可能會消耗大量記憶體,并導緻程式崩潰。

例如,要讀取一份檔案并傳回每行的字元數。若采用清單推導來做,則需把檔案每一行的長度都儲存在記憶體中。如果這個檔案特别大,或是通過無休止的network socket(網絡套接字)來讀取,那麼這種清單推導就會出問題。下面的這段清單推導代碼,隻适合處理少量的輸入值。

為了解決此問題,python提供了生成器表達式(generator expression),它是對清單推導和生成器的一種泛化(generalization)。生成器表達式在運作的時候,并不會把整個輸出序列都呈現出來,而是會估值為疊代器(iterator),這個疊代器每次可以根據生成器表達式産生一項資料。

把實作清單推導所用的那種寫法放在一對圓括号中,就構成了生成器表達式。下面給出的生成器表達式與剛才的代碼等效。二者的差別在于,對生成器表達式求值的時候,它會立刻傳回一個疊代器,而不會深入處理檔案中的内容。

以剛才傳回的那個疊代器為參數,逐次調用内置的next函數,即可使其按照生成器表達式來輸出下一個值。可以根據自己的需要,多次指令疊代器根據生成器表達式來生成新值,而不用擔心記憶體用量激增。

使用生成器表達式還有個好處,就是可以互相組合。下面這行代碼會把剛才那個生成器表達式所傳回的疊代器用作另外一個生成器表達式的輸入值。

外圍的疊代器每次前進時,都會推動内部那個疊代器,這就産生了連鎖效應,使得執行循環、評估條件表達式、對接輸入和輸出等邏輯都組合在了一起。

上面這種連鎖生成器表達式,可以迅速在python中執行。如果要把多種手法組合起來,以操作大批量的輸入資料,那最好是用生成器表達式來實作。隻是要注意:由生成器表達式所傳回的那個疊代器是有狀态的,用過一輪之後,就不要反複使用了(參見本書第17條)。

要點

當輸入的資料量較大時,清單推導可能會因為占用太多記憶體而出問題。

由生成器表達式所傳回的疊代器,可以逐次産生輸出值,進而避免了記憶體用量問題。

把某個生成器表達式所傳回的疊代器,放在另一個生成器表達式的for子表達式中,即可将二者組合起來。

串在一起的生成器表達式執行速度很快。