寫在前面:内容參照自《Effective Python》,其實你完全可以直接去看書,什麼?你不想自己看書,那麼你也可以關注我,我會不定期從書中挑出常用到的有效方法分享出來,這樣你就可以一邊刷頭條,一邊學習知識,豈不美哉。
正文
如果函數要産生一系列結果,那麼最簡單的做法就是把這些結果都放在一份清單裡,并将其傳回給調用者。例如,我們要查出字元串中每個詞的首字母,在整個字元串裡的位置。下面這段代碼,用 append 方法将這些詞的首字母索引添加到 result 清單中,并在函數結束時将其傳回給調用者。

輸入一些範例值,以驗證該函數能夠正常運作:
上面的 index_words 函數有兩個問題。
第一個問題是,這段代碼寫得有點擁擠。每次找到新的結果,都要調用 append 方法。但我們真正應該強調的,并不是對 result.append 方法的調用,而是該方法給清單中添加的那個值,也就是 index+1。另外,函數首尾還各有一行代碼用來建立及傳回 result 清單。于是,在函數主體部分的約 130 個字元(不計空白字元)裡,重要的大概隻有 75 個。
這個函數改用生成器(generator)來寫會更好。生成器是使用 yield 表達式的函數。調用生成器函數時,它并不會真的運作,而是會傳回疊代器。每次在這個疊代器上面調用内置的 next 函數時,疊代器會把生成器推進到下一個 yield 表達式那裡。生成器傳給 yield 的每一個值,都會由疊代器傳回給調用者。
下面的這個生成器函數,會産生和剛才那個函數相同的效果:
這個函數不需要包含與 result 清單互相動的那些代碼,因而看起來比剛才那種寫法清晰許多。原來那個 result 清單中的元素,現在都分别傳給 yield 表達式了。調用該生成器後所傳回的疊代器,可以傳給内置的 list 函數,以将其轉換為清單(關于生成器可以檢視:python 學習 生成器)。
index_words 函數的第二個問題是,它在傳回前,要先把所有結果都放在清單裡面。如果輸入量非常大,那麼程式就有可能耗盡記憶體并崩潰。相反,用生成器改寫後的版本,則可以應對任意長度的輸入資料。
下面定義的這個生成器,會從檔案裡面依次讀入各行内容,然後逐個處理每行中的單詞,并産生相應結果。該函數執行時所耗的記憶體,由單行輸入值的最大字元數來界定。
運作這個生成器函數,也能産生和原來相同的效果。
要點
- 使用生成器比把收集到的結果放入清單裡傳回給調用者更加清晰。
- 由生成器函數所傳回的那個選代器,可以把生成器函數體中,傳給 yield 表達式的那些值,逐次産生出來。
- 無論輸入量有多大,生成器都能産生一系列輸出,因為這些輸入量和輸出量,都不會影響它在執行時所耗的記憶體。