Python のジェネレータ関数と yield
ジェネレータ関数は yield を使って値を逐次生成します。大量のデータをメモリに載せずに処理できるため、効率的なイテレーション処理が可能です。
基本構文
def count_up(n):
for i in range(n):
yield i
for num in count_up(5):
print(num) # 0, 1, 2, 3, 4
yield に到達すると値を返して一時停止し、次の反復で再開します。
リストとの違い
# リスト:全要素をメモリに保持
def get_list(n):
return [i for i in range(n)]
# ジェネレータ:必要な時に生成
def get_generator(n):
for i in range(n):
yield i
100 万件のデータを処理する場合、リストはすべてをメモリに載せますが、ジェネレータは 1 件ずつ処理します。
ジェネレータ式
リスト内包表記と同様の構文で、丸括弧を使います。
gen = (x ** 2 for x in range(10))
print(next(gen)) # 0
print(next(gen)) # 1
yield from
別のイテラブルから値を委譲できます。
def flatten(nested):
for item in nested:
if isinstance(item, list):
yield from flatten(item)
else:
yield item
data = [1, [2, 3, [4, 5]], 6]
print(list(flatten(data))) # [1, 2, 3, 4, 5, 6]
send と双方向通信
ジェネレータに値を送り込むこともできます。
def accumulator():
total = 0
while True:
value = yield total
if value is not None:
total += value
gen = accumulator()
next(gen) # ジェネレータを起動
print(gen.send(10)) # 10
print(gen.send(20)) # 30
ジェネレータはメモリ効率の良いデータ処理、無限シーケンス、パイプライン処理などに活用できます。