注: この投稿では、Python 3.x 構文を想定しています。†
ジェネレーターは、 を呼び出すことができるオブジェクトを返す単純な関数であり、呼び出しnext
ごとに何らかの値を返し、StopIteration
例外を発生させて、すべての値が生成されたことを通知します。このようなオブジェクトはiteratorと呼ばれます。
return
Java と同様に、通常の関数は を使用して単一の値を返します。ただし、Python には、 と呼ばれる代替手段がありyield
ます。関数内の任意の場所で使用yield
すると、ジェネレーターになります。次のコードを確認してください。
>>> def myGen(n):
... yield n
... yield n + 1
...
>>> g = myGen(6)
>>> next(g)
6
>>> next(g)
7
>>> next(g)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
StopIteration
ご覧のとおり、はとmyGen(n)
を生成する関数です。すべての値が生成されるまで、を呼び出すたびに単一の値が生成されます。バックグラウンドで呼び出しをループするため、次のようになります。n
n + 1
next
for
next
>>> for n in myGen(6):
... print(n)
...
6
7
同様に、特定の一般的なタイプのジェネレーターを簡潔に記述する手段を提供するジェネレーター式があります。
>>> g = (n for n in range(3, 5))
>>> next(g)
3
>>> next(g)
4
>>> next(g)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
StopIteration
ジェネレータ式はリスト内包表記によく似ていることに注意してください。
>>> lc = [n for n in range(3, 5)]
>>> lc
[3, 4]
ジェネレーター オブジェクトは 1 回生成されますが、そのコードは一度にすべて実行されるわけではないことに注意してください。next
実際にコード (の一部) を実行するための呼び出しのみ。ジェネレーター内のコードの実行はyield
、値を返すステートメントに到達すると停止します。next
次に then を呼び出すと、最後のyield
. これは通常の関数との根本的な違いです。これらの関数は常に「先頭」で実行を開始し、値を返すとその状態を破棄します。
この件については、他にも言いたいことがあります。たとえば、send
データをジェネレーターに戻すことができます (参照)。しかし、それは、ジェネレーターの基本的な概念を理解するまで調べないことをお勧めします。
なぜジェネレーターを使用するのですか? 正当な理由がいくつかあります。
- 特定の概念は、ジェネレーターを使用してより簡潔に記述できます。
- 値のリストを返す関数を作成する代わりに、オンザフライで値を生成するジェネレーターを作成できます。これは、リストを構築する必要がないことを意味します。つまり、結果として得られるコードのメモリ効率が向上します。このようにして、単純に大きすぎてメモリに収まらないデータ ストリームを記述することさえできます。
ジェネレーターを使用すると、無限のストリームを自然に記述することができます。たとえば、フィボナッチ数を考えてみましょう:
>>> def fib():
... a, b = 0, 1
... while True:
... yield a
... a, b = b, a + b
...
>>> import itertools
>>> list(itertools.islice(fib(), 10))
[0, 1, 1, 2, 3, 5, 8, 13, 21, 34]
このコードはitertools.islice
、無限ストリームから有限数の要素を取得するために使用します。モジュール内の関数をよく確認することをお勧めします。これらの関数は、itertools
高度なジェネレータを非常に簡単に作成するための不可欠なツールであるためです。
† Python <=2.6 について:上記の例のは、指定されたオブジェクトnext
のメソッドを呼び出す関数です。Python <=2.6 では、の代わりに__next__
、わずかに異なる手法を使用します。Python 2.7 には呼び出しがあるため、2.7 では以下を使用する必要はありません。o.next()
next(o)
next()
.next
>>> g = (n for n in range(3, 5))
>>> g.next()
3