Python 中的生成器
本文將說明 Python 中的生成器。
YouTube Video
Python 中的生成器
概述
Python 的生成器是一種迭代器,也是用於高效進行重複處理的強大功能。它們可以讓你在處理大量資料時撰寫節省記憶體的程式碼。
什麼是生成器?
Python 中的生成器是一種特殊函數,使用 yield 關鍵字定義,一次產生一個值。其特點是暫停執行時會保留狀態,之後可以繼續執行。
yield 的基本用法
yield 是一個關鍵字,返回一個值並同時暫停函數執行。
1def simple_generator():
2 yield 1
3 yield 2
4 yield 3
5
6gen = simple_generator()
7
8print(next(gen)) # 1
9print(next(gen)) # 2
10print(next(gen)) # 3- 當呼叫時,此函數會返回一個每次產生一個值的生成器對象。
- 如果在沒有下一個值時調用
next(),將會發生StopIteration錯誤。
next() 與 StopIteration
1def simple_generator():
2 yield 1
3 yield 2
4 yield 3
5
6gen = simple_generator()
7
8try:
9 while True:
10 value = next(gen)
11 print(value)
12except StopIteration:
13 print("Finished")- 通過像這樣明確處理
StopIteration錯誤,可以檢測生成器什麼時候結束。
send(value)
調用 send(value) 會讓生成器恢復運行,並將 value 傳遞到 yield 表達式的位置。傳遞的值可在生成器端作為 yield 表達式的返回值接收。在第一次調用時,send(value) 只能傳送 None,因此必須使用 next() 或 send(None)。
1def gen():
2 x = yield 1
3 print(f"x = {x}")
4 y = yield 2
5 print(f"y = {y}")
6
7g = gen()
8print(next(g)) # -> 1 (value from yield 1)
9print(g.send(10)) # -> x = 10, 2 (value from yield 2)
10print(g.send(20)) # -> y = 20, StopIteration occurs- 通過
send(10),生成器的yield會變成返回 10 的表達式,且 10 會被賦值給x。
throw()
調用 throw 會讓生成器恢復運行,並在暫停的 yield 位置拋出異常。你可以在生成器內部處理異常來繼續執行。如果異常未被捕捉,則會向外傳播並結束生成器。
1def gen():
2 try:
3 yield 1
4 except ValueError as e:
5 print(f"Caught: {e}")
6 yield "recovered"
7
8g = gen()
9print(next(g)) # -> 1
10print(g.throw(ValueError("boom"))) # -> Caught: boom, "recovered"- 在這段程式碼中,通過調用
throw來將異常注入生成器。在生成器端,異常被處理並返回recovered。
close()
調用 close() 會終止生成器。在生成器內部,可以使用 finally 進行清理操作。在調用 close() 之後再調用 next() 或 send() 會拋出 StopIteration 錯誤。
1def gen():
2 try:
3 yield 1
4 finally:
5 print("Cleaning up...")
6
7g = gen()
8print(next(g)) # -> 1
9g.close() # -> Cleaning up...- 這段程式碼展示了調用
close()會終止生成器,並觸發finally區塊的清理過程。
yield from
yield from 是用於委託給子生成器的語法。這是一種在生成器內部調用另一個生成器,並將其所有的值傳遞到外部範圍的簡單方式。
1def sub_gen():
2 yield 1
3 yield 2
4
5def main_gen():
6 yield from sub_gen()
7 yield 3
8
9print(list(main_gen())) # -> [1, 2, 3]- 這段程式碼透過
yield from將所有子生成器的值委託給外部生成器,然後再產生3。
與迭代器的關係
生成器內部實作了 __iter__() 與 __next__(),因此本質上是一種類型的迭代器。因此,它們完全相容於 for 迴圈等可疊代操作。
與 for 迴圈的結合
在 Python 中,for 迴圈內部自動使用 next() 來取得值。
1def simple_generator():
2 yield 1
3 yield 2
4 yield 3
5
6for value in simple_generator():
7 print(value)透過這種方式,StopIteration 的處理也是自動化的。
建立無限生成器
1def count_up(start=0):
2 while True:
3 yield start
4 start += 1
5
6counter = count_up()
7print(next(counter)) # 0
8print(next(counter)) # 1可以建立無限循環的生成器,但使用時必須小心。
生成器表達式
生成器表達式使用括號編寫,語法與列表推導式相似,可以用來定義生成器。
1# List comprehension (generates the entire list at once)
2squares_list = [x**2 for x in range(5)]
3print(squares_list)
4
5# Generator expression
6squares_gen = (x**2 for x in range(5))
7for square in squares_gen:
8 print(square)與列表推導式不同,生成器表達式不會將所有元素一次載入記憶體,因此更為節省記憶體。
生成器的錯誤處理
生成器中可能會發生例外狀況。這種情況下,你可以像一般 Python 程式碼一樣使用 try-except。
1def safe_divide_generator(numbers, divisor):
2 """Yields results of dividing numbers by a given divisor safely."""
3 for number in numbers:
4 try:
5 yield number / divisor # Attempt to divide and yield result.
6 except ZeroDivisionError:
7 yield float('inf') # Return infinity if division by zero occurs.
8
9# Example usage
10numbers = [10, 20, 30]
11gen = safe_divide_generator(numbers, 0) # Create generator with divisor as 0.
12for value in gen:
13 print(value) # Output: inf, inf, inf在此範例中,針對除以零的情形進行了適當的錯誤處理。
生成器的堆疊追蹤
如果生成器內部發生異常,則在恢復生成器時會引發該異常。
1def error_generator():
2 """A generator that yields values and raises an error."""
3 yield 1
4 raise ValueError("An error occurred") # Raise a ValueError intentionally.
5 yield 2
6
7gen = error_generator()
8print(next(gen)) # Output: 1 (first value yielded)
9try:
10 print(next(gen)) # Attempt to get the next value, which raises an error
11except ValueError as e:
12 print(e) # Output: An error occurred (exception message is printed)- 這個生成器首先返回
1。恢復時引發的錯誤會被捕獲並作為錯誤訊息顯示。
生成器的使用範例
按行讀取檔案(適用於大型檔案)
1def read_large_file(filepath):
2 with open(filepath, 'r') as f:
3 for line in f:
4 yield line.strip()- 此函數使用疊代器逐行讀取文本檔,去除每行的空白字元,並以生成器形式返回,使得大型檔案可以用較低的記憶體消耗來處理。
費波那契數列生成器
1def fibonacci(limit):
2 a, b = 0, 1
3 while a < limit:
4 yield a
5 a, b = b, a + b
6
7for n in fibonacci(100):
8 print(n)- 此程式碼使用生成器來依序產生小於上限的斐波那契數,並通過
for迴圈將它們輸出。
應用情境
生成器還可以用於以下情境。
- 大型 CSV 或日誌檔的序列處理
- API 分頁
- 串流資料處理(例如:Kafka、IoT 裝置)
總結
| 概念 | 重點 |
|---|---|
yield |
暫停並返回值 |
| 生成器函數 | 內容含有 yield,呼叫時會返回迭代器的函數 |
| 優點 | 節省記憶體,適合處理大量資料集 |
| 生成器表達式 | 提供如 (x for x in iterable) 的簡潔語法 |
透過生成器,你可以在節省記憶體並保持程式簡潔的同時高效處理大量資料集。
您可以在我們的 YouTube 頻道上使用 Visual Studio Code 來跟隨上述文章一起學習。 請也查看我們的 YouTube 頻道。