Pythonのジェネレータは、データが必要なタイミングで要素を一つずつ作り、メモリ効率や処理効率を大幅に向上させる強力な仕組みです。この機能を理解することで、大量データの逐次処理や無限シーケンスの生成がスムーズになります。本記事では、基本構文からメリット、具体的な活用例までを解説していきます!
ジェネレータとは何か?
ジェネレータは「イテレータを簡単に書くための構文」です。通常のイテレータを実装する場合は、__iter__() と __next__() を持つクラスを定義し、iter() や next() で要素を取り出しますが、次に説明するジェネレータ関数を使えば yield キーワード一つで同じ動作を実現できます。
従来のイテレータは全要素をあらかじめリストなどに保持し、順番に返します。一方ジェネレータは必要なときに値を「遅延生成」し、前回の呼び出し位置から処理を再開できる点が最大の特徴です。
基本構文と使い方
ジェネレータ関数
ジェネレータ関数では return の代わりに yield キーワードを使用します。
yieldは関数の処理を一時停止し、呼び出し元に一つの値を返します。- 次に再開するときは、一時停止した直後の行から実行を続行します。
 returnは即座に関数を終了させて値を返しますが、yieldはあくまで「一時停止」のために使われます。
def count_up(n):
    for i in range(n):
        yield i
上記の関数を呼び出すとオブジェクトである <generator object count_up> が返り、next() または for 文を利用して一つずつ 0 から n-1 までの値を取り出せます。
ジェネレータ式
リスト内包表記に似た構文で、丸括弧 () を使うだけでジェネレータを定義できます。メモリを節約したまま簡潔に書けるため、小規模なフィルタや変換処理に向いています。
even_numbers = (i for i in range(1000000) if i % 2 == 0)
ジェネレータを使うメリット
ジェネレータを使うメリットは以下のような点があげられます。
- メモリ効率が高い
リストなど全データを保持せず、必要な要素だけを生成するため大容量データを扱う際に威力を発揮します。 - 遅延評価によるパフォーマンス向上
計算コストの高い処理の結果を必要になるまで待機できるため、全結果を一度に生成するオーバーヘッドを防げます。 - 無限シーケンスの生成
while Trueと組み合わせると無限に要素を作り続けることができ、breakを使って必要な分だけ取り出せます。 
具体的な活用例
ファイルの逐次読み込み
大容量ログファイルを一度に読み込むとメモリ不足になる場合があります。ジェネレータを使えば一行ずつ処理できるため安定して解析可能です。
def read_lines(path):
    with open(path, encoding='utf-8') as f:
        for line in f:
            yield line.rstrip('\n')
for row in read_lines('large.log'):
    process(row)
数値データの段階的生成
数値計算やグラフ描画など、段階的にデータを生成して後続処理に渡す場合に最適です。
def fib_generator(limit):
    a, b = 0, 1
    while a < limit:
        yield a
        a, b = b, a + b
Webスクレイピングの逐次処理
大量ページを次々とクロールし、ページ単位でパースと保存を繰り返す場合にも、メモリを最小限に抑えられます。
ジェネレータ利用時の注意点
ジェネレータを利用する時に注意すべき点は以下のような点です。
- StopIteration の取り扱い
next()を呼び出して要素が尽きるとStopIterationが発生します。for文を使うと自動的に捕捉してくれますが、手動で呼ぶ場合は例外処理を行うなどの対策が必要です。 - 一度使い切ると再利用できない
StopIteration に到達すると消費済みになるため、再度同じシーケンスを使う場合は再びジェネレータを生成する必要があります。 
まとめ
Pythonのジェネレータは、yield キーワードを使ってイテレータを簡潔に実装し、メモリ効率と処理効率を飛躍的に向上させる強力な機能です。大容量データの逐次処理や無限シーケンス生成、パイプライン構築まで幅広い場面で活用できます。ぜひ日々の開発に取り入れ、コードの性能と可読性を同時に高めましょう!

  
  
  
  
