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
ループと組み合わせる
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 間で保持される。複雑なステートマシンも書ける。
ジェネレータ関数は、データのストリーム処理やパイプライン構築に非常に便利です。