Python のジェネレータ関数の作り方

ジェネレータ関数は、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

ループと組み合わせる

forwhile ループの中で 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 間で保持される。複雑なステートマシンも書ける。

ジェネレータ関数は、データのストリーム処理やパイプライン構築に非常に便利です。