專項練習 · 9 分鐘閱讀

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])

需要時再物化

你的練習清單

常見問題

生成器建立時就會執行嗎?

不會。呼叫生成器函式只建立物件;推進生成器時函式體才執行。

生成器可以是無限的嗎?

可以。count 和 while True 的生成器都可能無限。用 islice、break 或其他停止條件加上邊界。

生成器一定比列表快嗎?

它可能更省記憶體,也避免建構中間資料,但速度取決於具體工作。應該測量,而不是假設。

練習生成器

12 道題,覆蓋 yield、next、惰性表達式和生成器耗盡。

打開練習

繼續閱讀: