pikesaku’s blog

個人的な勉強メモです。記載内容について一切の責任は持ちません。

Pythonのジェネレータ関数

ジェネレータ関数とは?

 

  • ジェネレータオブジェクトを生成する関数
  • ジェネレータオブジェクトはイテレータ処理(データを順番に処理)をする機能を持つ
  • オブジェクト生成時にデータを全て読み込まない。__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に引数に指定したオブジェクトが入り処理が再開。
 

  • 参考

Pythonのイテレータ、ジェネレータまわりの言語仕様について復習してみる - Qiita
 
 

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例外が発生