Python | ジェネレータ関数の使い方

2021年5月26日

公開日:2021/5/26

Pythonでは,ジェネレータ関数を用いるとメモリの使用量を減らすことができる.そのため,非常に大きなデータを操作しなければならないとき,ジェネレータ関数が有用になる.ただし,処理が遅くなる欠点もある.ジェネレータ関数は,ユーザー定義関数のように定義をし,yieldが用いられる特徴がある.以下にジェネレータ関数を用いた構文を記載する.

◆実施環境

Python 3.8.8

■for文を用いたジェネレータ関数の作成1

ジェネレーター関数を作成するには,通常のユーザー定義関数と同じように,defの後ろに関数名を定義する.関数名の後ろには”():”を加え,次行インデントした上で,yieldを記載する.yieldの後ろの値は要求の度に返される.

def ジェネレータ関数():
  yield x

以下では2行目に”generator”という名のジェネレータ関数を作成し,3行目でprint関数の処理を実装した.5行目にyieldを記載した.n = next(gen)でジェネレーター関数を呼び出し,yieldの変数が返る.print(n)で出力される.

# 1の処理 
def generator(i): 
  print('Good morning') 
  for n in range(i): 
    yield f'Good afternoon:{n}' # nの回数が変化 

gen = generator(3) 
n = next(gen) # ジェネレーター関数を呼びだし,"Good morning"が出力 
print(n) # "Good afternoon:0"が出力 

n = next(gen) 
print(n) # "Good afternoon:1"が出力 

n = next(gen) 
print(n) # "Good afternoon:2"が出力 

n = next(gen) # "エラー(StopIteration)"が出力

■実行結果

# 1の結果 
Good morning 
Good afternoon:0 
Good afternoon:1 
Good afternoon:2 
StopIteration # エラー出力

■for文を用いたジェネレータ関数の作成2

# 1の処理
def generator(i):
  print('Good morning')
  for n in range(i):
    yield f'Good afternoon:{n}' 

gen = generator(5) 
for x in gen: 
print(x)

■実行結果

# 1の結果Good morning 
Good afternoon:0 
Good afternoon:1 
Good afternoon:2 
Good afternoon:3 
Good afternoon:4

■while文を用いたジェネレータ関数の作成

# 1の処理
def generator(i): 
  print('Good morning') 
  n = 0 
  while n < i: 
    yield f'Good afternoon:{n}' 
    n += 1 

gen = generator(6) 
for x in gen: 
  print(x)

■実行結果

# 1の結果
Good morning 
Good afternoon:0 
Good afternoon:1 
Good afternoon:2 
Good afternoon:3 
Good afternoon:4 
Good afternoon:5

■ジェネレータ関数でのsendの利用

sendを利用してyieldに値を送ることができる

# 1の処理
def generator(i):
  print('Good morning')
  for n in range(i):
    x = yield n
    print(x)
    print('Good evening')

gen = generator(10)
next(gen) # "Good morning"が出力

next(gen) # None, "Good evening"が出力

gen.send(8) # 8がxに送られ,8と"Good evening"が出力

■実行結果

# 1の結果
Good morning
None
Good evening
8
Good evening

■ジェネレータ関数でのthrowの利用

throwを利用してエラーを意図的に発生させることができる.なお,#1の処理では通常のエラー(NameError)を出力し,#2の処理ではtry~excepを利用した例外処理を実装した.

# 1の処理(エラー発生)
def generator(i): 
  print('Good morning') 
  for n in range(i): 
    x = yield n 
    print(x) 
    print('Good evening') 

gen = generator(10) 
next(gen) 
gen.throw(NameError('名前が見つからない')) 

# 2の処理(try~exceptで例外処理) 
def generator(i): 
  print('Good morning') 
  for n in range(i): 
    try: 
      x = yield n 
      print(x) 
      print('Good evening') 
    except NameError as e: 
      print(e, type(e)) 

gen = generator(10) 
next(gen) 
gen.throw(NameError('名前が見つからない'))

■実行結果

# 1の結果
Good morning
Traceback (most recent call last): # 以降のエラーは途中省略
NameError: 名前が見つからない # エラーの最後の文

# 2の結果
Good morning
名前が見つからない <class 'NameError'>

■ジェネレータ関数でのcloseの利用

closeを利用してジェネレータ関数を終了させることができる.

# 1の処理 
def generator(i): 
  print('Good morning') 
  for n in range(i): 
    x = yield n 
    print(x) 
    print('Good evening') 

gen = generator(10) 
next(gen) 
gen.close() # 処理を終了させる 
next(gen) # エラーが発生する

■実行結果

# 1の結果 
Good morning 
StopIteration

以上