生成器概述
生成器是 Python 中的一種特殊迭代器,通過普通函數的語法實現,但使用 yield
語句返回數據。生成器自動實現了 __iter__()
和 __next__()
方法,因此可以直接用于迭代。生成器的核心特點是延遲計算(lazy evaluation),即只在需要時生成下一個值,而不是一次性計算并存儲所有值。
生成器的作用與優勢
作用:
- 節省內存空間
- 按需生成數據項
- 支持無限序列生成
- 簡化復雜迭代邏輯的代碼
優勢:
- 內存效率高,適用于大數據集
- 可以創建惰性求值的數據流
- 代碼結構更加簡潔易讀
生成器的使用場景
場景 | 描述 |
---|---|
處理大數據集 | 當數據量非常大以至于無法全部加載到內存中時,生成器可以逐個生成數據項 |
創建無限序列 | 如自然數列、斐波那契數列等理論上沒有終點的數據流 |
簡化代碼結構 | 在某些情況下,使用生成器可以讓代碼更加簡潔、易維護 |
生成器表達式 vs 列表推導式
類型 | 語法 | 是否立即執行 | 示例 | 特點 |
---|---|---|---|---|
列表推導式 | 使用方括號 [] | ? 是 | [i * 5 for i in range(5)] | 立即計算結果并保存在內存中 |
生成器表達式 | 使用圓括號 () | ? 否 | (i * 5 for i in range(5)) | 延遲計算,每次迭代才會生成一個值 |
示例代碼:
li = [i * 5 for i in range(5)]
print(li) # 輸出: [0, 5, 10, 15, 20]gen = (i * 5 for i in range(5))
print(gen) # 輸出: <generator object ...>
print(gen.__next__()) # 輸出: 0
生成器函數:yield 的作用
帶有 yield
關鍵字的函數稱為生成器函數。它不像普通函數那樣返回一個值后就結束,而是可以在多個調用之間“暫停”和“恢復”。類似于中斷函數。
yield 的特點:
- 每次調用
next()
會從上次yield
的位置繼續執行 - 保留函數的狀態
- 返回值不會被一次性計算出來,而是按需生成
示例代碼:
def test():yield 'a'yield 'b'yield 'c'gen = test()
print(gen.__next__()) # 輸出: a
print(gen.__next__()) # 輸出: b
print(gen.__next__()) # 輸出: c
可迭代對象、迭代器、生成器三者關系
名稱 | 定義 | 特點 |
---|---|---|
可迭代對象(Iterable) | 實現了 __iter__() 方法的對象 | 可以用 for...in 遍歷,如 list 、str 、dict 、迭代器、生成器 |
迭代器(Iterator) | 實現了 __next__() 方法的對象 | 可以使用 next() 獲取下一個元素 |
生成器(Generator) | 一種特殊的迭代器,由 yield 函數或生成器表達式產生 | 自動實現 __iter__() 和 __next__() |
三者關系圖示:
- 可迭代對象包含迭代器迭代器包含生成器
可迭代對象 ? 迭代器 ? 生成器
實戰對比:列表 vs 生成器處理大數據
比較兩種方式處理一千萬個數字(0~9999999),并對每個數字進行平方操作。
使用模塊:
time
:用于計時sys
:用于查看內存占用
代碼如下:
import time
import sys# 方法一:使用列表
def use_list():start_time = time.time()numbers = [i for i in range(10_000_000)] # 生成列表squares = [x * x for x in numbers] # 計算平方end_time = time.time()print(f"【列表】耗時: {end_time - start_time:.4f} 秒")print(f"【列表】占用內存: {sys.getsizeof(numbers) + sys.getsizeof(squares)} 字節")# 方法二:使用生成器
def number_generator(n):for i in range(n):yield idef use_generator():start_time = time.time()gen = number_generator(10_000_000)squares = (x * x for x in gen) # 生成器表達式,不會立即計算count = 0for square in squares:count += 1 # 強制執行生成器end_time = time.time()print(f"【生成器】耗時: {end_time - start_time:.4f} 秒")print(f"【生成器】生成器對象本身占用內存: {sys.getsizeof(gen)} 字節")# 運行測試
print("=== 開始測試 ===\n")
use_list()
print("\n------------------------\n")
use_generator()
print("\n=== 測試結束 ===")
結果分析(根據機器性能不同,數值可能略有差異):
指標 | 列表方式 | 生成器方式 |
---|---|---|
內存占用 | 非常大(約 184MB) | 極小(約 112B) |
耗時 | 略快 | 略慢 |
是否適合大數據 | ? 不適合 | ? 非常適合 |
總結
生成器是一種強大而高效的工具,特別適用于:
- 數據量龐大的場景
- 需要延遲加載的場景
- 需要節省內存的場景
- 需要簡化復雜迭代邏輯的場景
雖然生成器在速度上略遜于列表,但它在內存使用上的優勢使其成為處理大規模數據的首選方式。