ジェネレータ: 遅延評価のイテレータ

巨大なデータを一度にメモリに載せず、必要な時に1つずつ生成。

ジェネレータ
yield で値を1つずつ返す関数。
yield
値を返して一時停止するキーワード。
遅延評価
必要になるまで計算しない仕組み。

ジェネレータとは?

貯水タンクと蛇口 (Water Tank vs Tap)

リストは「貯水タンク」です。水を1トン使うには、1トン入る巨大なタンクを用意しなければなりません(メモリ大量消費)。ジェネレータは「蛇口」です。配管から水(データ)が必要な分だけ流れてくるので、巨大なタンクは不要です。コップ1杯でもプール一杯でも、メモリ消費は蛇口の分だけです。

ジェネレータは `yield` を使って「値を1つ返して一時停止」します。次に呼ばれるまで待機するため、無限の数列や巨大なファイルの処理でも、メモリをほとんど使いません。Pythonにおいて「巨大データ処理」の切り札です。

Basic Generator
def count_up(start=0):
n = start
while True:
yield n
n += 1
counter = count_up()
print(next(counter)) # 0
print(next(counter)) # 1
print(next(counter)) # 2
Bad
# ❌ Bad: 全データをリストに読み込む
def read_all_lines(path):
with open(path) as f:
return f.readlines() # メモリに全行を保持
lines = read_all_lines("huge.log") # メモリ爆発!
Good
# ✅ Good: ジェネレータで1行ずつ
def read_lines(path):
with open(path) as f:
for line in f:
yield line.strip()
for line in read_lines("huge.log"): # メモリ効率的
process(line)

ジェネレータ式

Generator Expression
# ジェネレータ式(リスト内包表記の () 版)
squares_list = [x**2 for x in range(1000000)] # メモリに全部
squares_gen = (x**2 for x in range(1000000)) # 遅延評価
# メモリ使用量の比較
import sys
print(sys.getsizeof(squares_list)) # 約8MB
print(sys.getsizeof(squares_gen)) # 約200バイト
Tip: リスト内包表記は [ ]、ジェネレータ式は ( )

パイプライン処理

Chaining Generators
# パイプライン処理
def filter_even(nums):
for n in nums:
if n % 2 == 0:
yield n
def double(nums):
for n in nums:
yield n * 2
numbers = range(10)
result = double(filter_even(numbers))
print(list(result)) # [0, 4, 8, 12, 16]

合格ライン

yield でジェネレータを書ける
ジェネレータ式 () を使える
ジェネレータが使い捨てな理由を知っている

演習課題

課題1: yield
yield を使ってジェネレータを作成してください。
課題2: ジェネレータ式
ジェネレータ式で遅延評価を実装してください。