ジェネレータ関数とは?
- ジェネレータオブジェクトを生成する関数
- ジェネレータオブジェクトはイテレータ処理(データを順番に処理)をする機能を持つ
- オブジェクト生成時にデータを全て読み込まない。__next__メソッド実行時に、1つづつ読み込む。大きなデータ、ネットワーク通信処理時に省メモリの効果あり。
- yield式を含む。yield実行時に、呼び出し元にデータと制御を戻す
動作確認
- データ生成
$ seq 10000 | xargs -I {} echo "line {}" > ./data $ head -3 ./data line 1 line 2 line 3 $ tail -3 ./data line 9998 line 9999 line 10000
- サンプルコード
def gen(): for l in open('./data'): l = l.strip() yield l a = gen() for i in range(3): print(a.__next__())
- 実行結果
line 1 line 2 line 3
- 考察
__next__メソッドを呼び出す度にforループが1回回る。
__next__メソッド呼び出される迄は、処理待ち状態。
sendメソッド
処理待ち状態のジェネレータオブジェクトにデータを渡す。
- サンプルコード
def gen(): for l in open('./data'): l = l.strip() v = yield l if v is None: print('moge') else: yield l + ' ' + v a = gen() print('1 回目nextメソッド実行 ========================') print(a.__next__()) print() print('2 回目nextメソッド実行 ========================') print(a.__next__()) print() print('sendメソッド実行 ========================') print(a.send('huga')) print() print('3 回目nextメソッド実行 ========================') print(a.__next__())
- 実行結果
1 回目nextメソッド実行 ======================== line 1 2 回目nextメソッド実行 ======================== moge line 2 sendメソッド実行 ======================== line 2 huga 3 回目nextメソッド実行 ======================== line 3
- 考察
v = yield lのように、yield結果をオブジェクトに入れる記述が必要。
nextメソッド実行時は、vにNoneが入る。yield実行時にデータと制御を呼び出し元に戻し処理は停止。
・1回目nextメソッド実行時に、moge出力なし
・2回目nextメソッド実行時に、初めてif v is None:が動作。この時のvはNone(1回目nextメソッド実行結果)
sendメソッド実行時は、vに引数に指定したオブジェクトが入り処理が再開。
- 参考
throwメソッド
処理待ち状態のジェネレータオブジェクトに例外を渡す。
- サンプルコード
def gen(): for l in open('./data'): l = l.strip() v = yield l if v is None: print('moge') else: yield l + ' ' + v a = (gen()) print('1 回目nextメソッド実行 ========================') print(a.__next__()) print() print('2 回目nextメソッド実行 ========================') print(a.__next__()) print() print('throwメソッド実行 ========================') print(a.throw(ValueError('error occur'))) print() print('3 回目nextメソッド実行 ========================') print(a.__next__())
- 実行結果
1 回目nextメソッド実行 ======================== line 1 2 回目nextメソッド実行 ======================== moge line 2 throwメソッド実行 ======================== Traceback (most recent call last): File "./a.py", line 21, in <module> print(a.throw(ValueError('error occur'))) File "./a.py", line 4, in gen v = yield l ValueError: error occur
closeメソッド
処理待ち状態のジェネレータオブジェクトにGeneratorExit例外を渡し、ジェネレータ処理を正常終了させる。
- サンプルコード
def gen(): for l in open('./data'): l = l.strip() v = yield l if v is None: print('moge') else: yield l + ' aaa ' + v a = (gen()) print('1 回目nextメソッド実行 ========================') print(a.__next__()) print() print('2 回目nextメソッド実行 ========================') print(a.__next__()) print() print('closeメソッド実行 ========================') print(a.close()) print() print('3 回目nextメソッド実行 ========================') print(a.__next__())
- 実行結果
1 回目nextメソッド実行 ======================== line 1 2 回目nextメソッド実行 ======================== moge line 2 closeメソッド実行 ======================== None 3 回目nextメソッド実行 ======================== Traceback (most recent call last): File "./a.py", line 25, in <module> print(a.__next__()) StopIteration
- 考察
closeメソッド実行時に、forループは停止。
closeメソッド実行後のジェネレータオブジェクトにnextメソッド実行するとStopIteration例外が発生