ジェネレーターイテレーターとは
変数(これ)=ジェネレータ関数
でいう左辺、つまり、ジェネレータ関数を変数に収めたものです。
ジェネレータ関数は後述。
一般に関数の戻り値は、関数内で一通り計算し終わった後にreturn文によって受け取ります。
これに対して、ジェネレーターイテレーター用いることで、指定したタイミングで戻り値を返し一旦終了。次に関数を呼ぶと前回の続きからスタートし、再び戻り値を返してくれるものになってます。
メリット:例
関数の戻り値が、なんらかの計算の結果を格納した膨大なリストや辞書構造の場合、メモリ負荷につながります。結果をどこか、別サーバーのデータベースやファイルなどに格納するのであれば、各要素ごとにデータベースに登録しても、問題はないはずです。
この、各要素というのをジェネレーターイテレーターでもって取得することができます
ジェネレーター関数
関数内にyield式が入っている関数のことです。yield式とは、return 〇〇のように関数の戻り値を返すためのもの。
1 2 3 4 5 6 7 8 9 10 | #ジェネレーター関数 def calledCounter(): print('First time') yield 1 print('Second time') yield 2 print('Third time') yield 3 |
使い方
python2の場合
1 2 3 4 5 6 7 | def main(): count = calledCounter() #左辺がジェネレーターイテレーター、右辺がジェネレーター関数 print('count=%s' % count.next()) print('count=%s' % count.next()) print('count=%s' % count.next()) main() |
python3の場合、count.next()は、
1 | count.__next__() |
になります。
2行目:ジェネレーターイテレーター
冒頭で述べた通り、ジェネレーター関数を用いて、ジェネレーターイテレーター(count)を用意します。
3~5行目:戻り値の取得
ジェネレーター関数内の待機地点の更新と戻り値の取得を行います。
結果とタイミングについて
以下のように出力されます。
1 2 3 4 5 6 | First time count=1 Second time count=2 Third time count=3 |
count.next()によって、返ってくる値、つまり、呼ばれるyield式が変わっているのがわかると思います。
タイミングについて、もう少し詳しく見ていくと
1 2 3 | def main(): count = calledCounter() print('count=%s' % count.next()) |
count.next()を1回にした場合、
1 2 | First time count=1 |
と出力されました。
このことから、count.next()を呼ぶごとに次のyield式で待機することがわかります。
yieldの数 < next()の数 の場合エラー
次のyield式で待機することから、次のyield式がない場合 StopIteration というエラーが表示されます。今回であれば、yieldの数は3個なので、4回目のcount.next()を呼んでしまうとエラーになります。
一般的な使い方
前の使い方では、yield式を何回も差し込んでいる形なので、関数としての意味が薄いと思います。
for文を使うと意味合いが出ます。
1 2 3 4 5 6 7 8 9 10 | #ジェネレーター関数 def printName(): l = ['Alice', 'Bob', 'Charlie'] for name in l: #for文内でも待機できる yield name def main(): names = printName() for n in names: #for文によってnames.next()を呼ぶ必要がない print(n) |
結果は以下の通り、
1 2 3 | Alice Bob Charlie |
9行目:for文によって、すべてのyieldを返す
これによって、いちいちyieldの数とnext()の数を気にする必要がありません。
最後に
本来は有用性について、もっと具体例を交えるべきですが、内容が広がるので仕方なく簡略して説明しました。有効な場面があるので、頭の片隅にでも置いてもらえれば、と思います。
参考
https://qiita.com/tomotaka_ito/items/35f3eb108f587022fa09