ジェネレーター¶
ジェネレーターは関数を一時停止させ、途中経過の結果を返すことができる機能です。
通常、関数やメソッドは結果を返す時にはreturn
を利用しますが、それをyield
に変えることでジェネレーターにできます。
# 普通の関数
def normal_function():
return 'hello world'
# ジェネレーター
def generator():
yield 'hello world'
ジェネレーターを利用することで、関数の途中まで進めて結果を返し、任意のタイミングで処理を再開できます。
例:
def generator(n):
for i in range(n):
yield 'hello_{}'.format(i)
# ジェネレーターを実行するとジェネレーターオブジェクトが返ってきます。
gen = generator(3)
# nextを使って途中結果を取得
value = next(gen)
print(value) # => 'hello_0'
value = next(gen)
print(value) # => 'hello_1'
value = next(gen)
print(value) # => 'hello_2'
# 返せるものがなくなると StopIteration が発生します。
next(gen) # => 例外 StopIteration が発生
最初に yield
が呼ばれると関数はそこで処理が停止し、次にnext
が呼ばれるまでは実行されません。返すべき値がなくなった場合は StopIteration
の例外が発生します。
ジェネレーターオブジェクト
はlist
型などと同様に、for
ループで扱うことができます。
gen = generator(3)
for value in gen:
print(value)
実行結果:
hello_0
hello_1
hello_2
どのようなメリットがあるのか?¶
ジェネレーターは関数の中で処理を停止し、任意のタイミングで再開できます。つまり関数の中で状態を持つことができます。この特性を利用することで、巨大なデータを扱う際にメモリー使用量を節約することに活用できます。
例として、数値データが入ってるlist
のデータを引数で与えると、それぞれの数値を2乗して返すような関数を考えてみます。
def squaring_list(numbers):
""" numbersの各要素を二乗する """
result = []
for number in numbers:
result.append(numer ** 2)
return result
numbers = [1, 2, 3, 4, 5]
for square_number in squaring_list(numbers):
print(square_number)
実行結果:
1
4
9
16
25
squaring_list
関数はそれぞれの要素を2乗した結果をresult
という変数に一旦全て保存して最後にreturn
で返しています。
numbers
の要素数が10万、100万と膨大な数になると、result
には大量の計算結果が溜まっていき、その分だけメモリーの使用量が増大します。 最悪マシンのメモリーが足りなくなってプログラムが止まってしまう可能性もあります。
そこでメモリーを節約するために、ジェネレーターを活用します。
def squaring_list(numbers):
""" numbersの各要素を二乗する """
for number in numbers:
yield number ** 2
先ほどの関数との大きな違いは、for
ループの中で計算をしたらyieldですぐに結果を返すという点です。こうすることでresult
変数に計算結果を溜め込む必要がなくなるので、前の関数に比べてその分メモリーを節約できます。
またジェネレーター化しても、元の関数と利用方法がまったく同じにできるというのも嬉しい点です。
メモリーが節約できるのはジェネレーターを活用するほんの一例ですが、特性を理解して賢く使いましょう。
ジェネレーター式¶
ジェネレーターはyield
を使わずに式
として表現することもできます。簡単なものであれば、関数のスタイルよりも簡潔に記述できるので選択肢の1つとして覚えておくと良いでしょう。
numbers = [1, 2, 3, 4, 5]
# for文を () 囲むだけでジェネレーターが生成されます。
gen = (n ** 2 for n in numbers)
for square_number in gen:
print(square_number)
公式ドキュメント¶
詳細は、9.9. ジェネレータ (generator)、ジェネレータを参照してください