Python 生成器練習指南:惰性取值
生成器不只是新語法,它改變了執行方式:產生一個值,暫停,之後再繼續。當完整列表浪費記憶體、無法建構,或者根本不需要時,這種模型特別有用。
從帶 yield 的函式開始
生成器函式看起來和普通函式一樣,直到執行到 yield。此時它回傳一個值,並記住局部狀態。下一次請求從下一行繼續。想拿到全部值時用 for 迴圈,想一次取一個值時用 next。
def countdown(n):
while n > 0:
yield n
n -= 1
for value in countdown(3):
print(value)暫停與繼續
生成器只能消費一次
生成器物件是迭代器。耗盡之後再請求值會拋出 StopIteration。需要再次走訪資料時,要建立新的生成器。這一點和可以反覆走訪的列表不同。
def pair():
yield 1
yield 2
gen = pair()
print(list(gen))
print(list(gen))第二次走訪為空
用表達式寫短流水線
轉換邏輯簡單時,生成器表達式很緊湊。圓括號就夠了,不必定義具名函式。它仍然是惰性的,只有用 list 或 sum 包起來時才真正請求值。
squares = (n * n for n in range(4))
print(sum(squares))一行式惰性流水線
把多個階段串起來
一個生成器可以消費另一個生成器。這樣可以把讀取、篩選和格式化分開,而不必建構中間列表。每個階段保持短小;流水線變長時,給階段起清楚的名字。
def evens(values):
for value in values:
if value % 2 == 0:
yield value
print(list(evens(range(6))))一次處理一個階段
知道什麼時候該用列表
生成器用惰性記憶體換取隨機存取和可重複走訪。需要 len、下標、排序或多輪走訪時,就轉成列表。選擇資料結構要看下一步操作,而不是看哪種寫法最短。
values = list(n for n in range(3))
print(len(values))
print(values[1])需要時再物化
你的練習清單
- 值可以按需產生時使用 yield。
- 只需要一個值時再呼叫 next。
- 第二次走訪要建立新的生成器。
- 用 list、sum 或迴圈消費生成器表達式。
- 需要下標或長度時轉成列表。
常見問題
生成器建立時就會執行嗎?
不會。呼叫生成器函式只建立物件;推進生成器時函式體才執行。
生成器可以是無限的嗎?
可以。count 和 while True 的生成器都可能無限。用 islice、break 或其他停止條件加上邊界。
生成器一定比列表快嗎?
它可能更省記憶體,也避免建構中間資料,但速度取決於具體工作。應該測量,而不是假設。
練習生成器
12 道題,覆蓋 yield、next、惰性表達式和生成器耗盡。
繼續閱讀: