在Python中處理數據時,我們經常需要生成滿足特定條件的序列。列表推導式(List Comprehension)和生成器表達式(Generator Expression)是兩種常用工具,但它們的“工作方式”和“效率”卻有很大區別。理解兩者的差異,能幫你在數據處理時寫出更高效的代碼。
列表推導式:直接生成完整列表¶
列表推導式是最直觀的生成列表的方式,用中括號 [] 包裹,結構類似 [表達式 for 變量 in 可迭代對象 if 條件]。
示例:生成1到10的平方列表
squares_list = [x**2 for x in range(1, 11)]
print(squares_list) # 輸出: [1, 4, 9, 16, 25, 36, 49, 64, 81, 100]
特點:
- 立即計算:執行時會直接創建整個列表,所有元素都被加載到內存中。
- 內存佔用大:如果數據量很大(比如100萬條),列表會佔用大量內存。
- 可重複使用:列表生成後可多次遍歷(如 for num in squares_list 可以重複執行)。
- 支持隨機訪問:可以通過索引獲取任意元素(如 squares_list[5] 得到第6個元素)。
生成器表達式:惰性計算的“數據流”¶
生成器表達式用小括號 () 包裹,結構與列表推導式類似:(表達式 for 變量 in 可迭代對象 if 條件)。
示例:生成1到10的平方序列(生成器)
squares_generator = (x**2 for x in range(1, 11))
print(squares_generator) # 輸出: <generator object <genexpr> at 0x...>
特點:
- 惰性計算:生成器不會立即創建所有元素,而是“等待”被遍歷(迭代)時才逐個生成。
- 內存友好:僅在需要時生成元素,內存中只保留當前處理的元素,適合大數據量。
- 單向迭代:生成器只能遍歷一次,遍歷結束後無法“回頭”(類似文件指針,讀完即到末尾)。
- 無法隨機訪問:不能通過索引獲取元素,也無法直接用 len() 獲取長度。
核心區別:內存與效率¶
| 對比維度 | 列表推導式 | 生成器表達式 |
|---|---|---|
| 內存佔用 | 一次性加載所有元素,內存佔用大 | 惰性生成,僅存當前元素,內存佔用小 |
| 遍歷次數 | 可多次遍歷(重複迭代) | 僅能遍歷一次(用完即棄) |
| 隨機訪問 | 支持(通過索引) | 不支持(無索引) |
| 適用場景 | 小數據、需多次使用、隨機訪問 | 大數據、單次遍歷、內存敏感場景 |
實戰對比:大數據量下的效率¶
假設處理100萬個數,計算平方並求和:
列表推導式的內存壓力¶
import sys
# 生成100萬個元素的列表
big_list = [x**2 for x in range(1000000)]
print("列表佔用內存:", sys.getsizeof(big_list)) # 輸出: ~112字節(僅存儲列表結構)
# 實際元素總大小:每個int佔28字節,100萬約28MB,加上列表結構額外佔用,總內存接近30MB
當數據量超過內存時,列表會導致 MemoryError(內存溢出)。
生成器表達式的內存優勢¶
big_generator = (x**2 for x in range(1000000))
print("生成器佔用內存:", sys.getsizeof(big_generator)) # 輸出: ~112字節(固定大小)
# 生成器僅存儲迭代狀態,無論數據量多大,內存佔用幾乎不變
# 迭代求和(逐個計算,內存中僅存當前元素)
total = sum(big_generator)
print(total) # 輸出: 333332833333500000
如何選擇?看需求!¶
- 用列表推導式:
- 需要多次使用結果(如反覆遍歷或存儲)
- 需要隨機訪問元素(如
list[5]) -
數據量小(內存無壓力)
-
用生成器表達式:
- 數據量極大(遠超內存)
- 只需逐個處理元素(如管道式數據處理)
- 無需重複使用結果,僅需“流式”處理
總結¶
列表推導式是“急脾氣”,直接把所有結果打包給你;生成器表達式是“慢性子”,按需逐步提供結果。大數據量下,生成器表達式能顯著節省內存,避免“內存爆炸”。記住:小數據用列表,大數據用生成器!