Python 入門|函式與資料處理
Python 生成式與產生器:使用函式的經典範例
生成式適合用精簡語法建立資料;產生器則能在需要時逐筆提供資料。本文透過函式示範串列、集合與字典生成式,以及使用 yield 和生成器運算式的經典寫法,並比較它們的執行方式。
一、生成式與產生器的差異
| 比較項目 | 生成式(Comprehension) | 產生器(Generator) |
|---|---|---|
| 主要用途 | 用簡潔語法建立或轉換串列、集合、字典。 | 逐筆產生資料,避免預先建立全部結果。 |
| 常見寫法 | [...]、{...} | 函式中的 yield,或 (...) 生成器運算式。 |
| 資料產生時機 | 通常立即建立完整結果。 | 取值時才逐筆產生,常用於迴圈或 next()。 |
| 常見情境 | 需要完整結果、索引或多次使用。 | 資料量大,或只需順序處理一次。 |
可以把生成式理解為「建立資料的語法」,把產生器理解為「逐筆供應資料的物件或機制」。兩者可以搭配函式使用,但用途和執行方式不同。
二、函式搭配生成式
範例 1:回傳平方數串列
def make_squares(numbers):
return [number ** 2 for number in numbers]
print(make_squares([1, 2, 3, 4, 5]))
# [1, 4, 9, 16, 25]
串列生成式會一次建立所有平方數,函式再用 return 回傳完整串列。
範例 2:篩選偶數
def get_evens(numbers):
return [number for number in numbers if number % 2 == 0]
print(get_evens(range(1, 11)))
# [2, 4, 6, 8, 10]
生成式尾端的 if 用來篩選資料,只有偶數會放進結果串列。
範例 3:建立平方對照字典
def make_square_map(numbers):
return {number: number ** 2 for number in numbers}
print(make_square_map(range(1, 6)))
# {1: 1, 2: 4, 3: 9, 4: 16, 5: 25}
字典生成式以冒號分隔鍵和值,適合建立查表資料。
範例 4:取得不重複的文字長度
def unique_lengths(words):
return {len(word) for word in words}
print(sorted(unique_lengths(["rice", "fish", "corn", "tea"])))
# [3, 4]
大括號中沒有鍵值冒號時,這是集合生成式。集合會自動去除重複值;範例以 sorted() 排序,讓結果容易閱讀。
三、產生器函式:使用 yield
範例 5:逐筆產生平方數
def generate_squares(n):
for number in range(1, n + 1):
yield number ** 2
for value in generate_squares(5):
print(value)
# 1
# 4
# 9
# 16
# 25
包含 yield 的函式稱為產生器函式。呼叫它時會得到產生器物件;每次迴圈取值,函式就執行到下一個 yield,交出一筆資料並暫停。
範例 6:逐筆篩選偶數
def generate_evens(numbers):
for number in numbers:
if number % 2 == 0:
yield number
for value in generate_evens(range(1, 11)):
print(value)
# 2
# 4
# 6
# 8
# 10
這個產生器不會先建立偶數串列,而是在迴圈需要時交出符合條件的數字。
範例 7:產生倒數數字
def countdown(start):
while start > 0:
yield start
start -= 1
for value in countdown(5):
print(value)
# 5
# 4
# 3
# 2
# 1
產生器會保留暫停時的區域變數狀態,所以再次取值時,start 會接續前一次的值。
範例 8:逐行讀取文字檔
def read_lines(file_path):
with open(file_path, encoding="utf-8") as file:
for line in file:
yield line.rstrip("\n")
for line in read_lines("data.txt"):
print(line)
每次只交出一行,適合逐行處理大型文字檔。使用 with 可在讀取結束後自動關閉檔案。
四、函式回傳生成器運算式
範例 9:回傳平方數生成器
def square_stream(numbers):
return (number ** 2 for number in numbers)
squares = square_stream(range(1, 6))
for value in squares:
print(value)
# 1
# 4
# 9
# 16
# 25
這個函式回傳的是生成器運算式。圓括號 () 不會立即建立完整串列,而是等到迴圈取值時逐筆計算。
範例 10:使用 next() 手動取值
def make_number_stream(n):
return (number for number in range(1, n + 1))
numbers = make_number_stream(3)
print(next(numbers)) # 1
print(next(numbers)) # 2
print(next(numbers)) # 3
每次呼叫 next() 會取出下一筆資料。資料全部取完後,再呼叫 next() 會引發 StopIteration;通常使用 for 迴圈會更方便。
五、怎麼選擇適合的寫法?
- 要一次取得完整結果,並可能重複使用:用串列生成式搭配
return。 - 要逐筆運算,並保留較複雜的流程狀態:用產生器函式搭配
yield。 - 只需簡單地逐筆轉換資料:函式可以回傳生成器運算式。
- 要建立鍵值查表或去除重複項目:分別考慮字典生成式或集合生成式。
重點記憶:生成式搭配 return 常會回傳已建立好的集合;產生器函式用 yield 暫停並逐筆交付資料;生成器運算式則可用簡短語法逐筆產生資料。
沒有留言:
張貼留言