ジェネレータ関数は、yield キーワードを使って値を順番に生成する関数です。普通の関数と見た目は似ていますが、動作が大きく異なります。
基本的な作り方
yield を含む関数を定義するだけで、ジェネレータ関数になります。
def simple_generator(): yield "A" yield "B" yield "C" gen = simple_generator() for value in gen: print(value) # A # B # C
関数を呼び出してもすぐには実行されず、ジェネレータオブジェクトが返されます。for 文や next() で要素を取り出すときに、初めて関数の中身が実行されます。
引数を受け取る
普通の関数と同様に、引数を受け取ることができます。
def repeat(value, times): for _ in range(times): yield value for v in repeat("Hello", 3): print(v) # Hello # Hello # Hello
ループと組み合わせる
for や while ループの中で yield を使うのが一般的なパターンです。
def fibonacci(limit): a, b = 0, 1 while a < limit: yield a a, b = b, a + b for num in fibonacci(100): print(num, end=" ") # 0 1 1 2 3 5 8 13 21 34 55 89
条件分岐と yield
条件に応じて異なる値を yield することもできます。
def filter_even(numbers): for n in numbers: if n % 2 == 0: yield n evens = filter_even([1, 2, 3, 4, 5, 6]) print(list(evens)) # [2, 4, 6]
return との併用
ジェネレータ関数で return を使うと、その時点でジェネレータが終了します。return に値を指定すると、StopIteration 例外の value 属性に格納されます。
def limited_counter(max_count): count = 0 while True: if count >= max_count: return "上限に達しました" yield count count += 1 gen = limited_counter(3) print(list(gen)) # [0, 1, 2]
ジェネレータ関数の特徴
遅延評価
値は必要になるまで生成されない。大量のデータを扱う際にメモリ効率が良い。
状態の保持
ローカル変数の状態が yield 間で保持される。複雑なステートマシンも書ける。
ジェネレータ関数は、データのストリーム処理やパイプライン構築に非常に便利です。