yieldの使いどころ

投稿者: | 2022-07-10

ここがわかりやすい
http://ailaby.com/yield/
https://tokitsubaki.com/python-yield-statement/598/

目次

return

  • 関数の処理を終了し、値を返す

yield

  • 関数の処理を一時停止し、値を返す(次の行から再開)
  • next(ジェネレーター) で次を取得する

yield は何に使う?

  • メモリ使用量の少ないスクリプトが書ける
  • 外側のループと、本来その内側に書いていた処理が別々にできる

例)

  • 1GBの巨大なテキストファイルを読み込み、データを渡してくれる関数があった場合
  • 受け渡し用のメモリが1GBという巨大なサイズになってしまう(returnの場合)
  • これをyieldを使うと、少量単位(1行ずつ等)で都度読み込めるためメモリは少なくてすむ

用例

例1)ループ
def func():
    a = 10
    b = 20
    c = a + b
    yield c      # ここで一旦停止
 
    a = 100
    b = 200
    c = a + b
    yield c      # ここで一旦停止
 
    a = 1000
    b = 2000
    c = a + b
    yield c      # ここで一旦停止
 
 
for x in func():
    print (x)
 
#---- 結果 -----
# 30
# 300
# 3000
#---------------

例2)ループ以外

gen = func()        # ジェネレータ
print (gen)
print (gen.__next__())  # 1 回目  # <- next(gen)でもよい
print (gen.__next__())  # 2 回目
print (gen.__next__())  # 3 回目
 
#---- 結果 -----
# <generator object func at 0x00000000145D1240>
# 30
# 300
# 3000
#-------------
def func():
  yield 'Hello'
  yield 'Python'

gen = func()

>>>print(next(gen))
'Hello'
>>>print(next(gen))
'Python'

ジェネレーター関数はそのままリストにできる

>>>list(gen)
['Hello', 'Python']

yieldの使いどころ

  • large.txtからyieldを使い1行だけ読み込む(ここをループ)
  • 1行だけ読み込まれたlineに対し、処理を行う
  • forで次の行を読み込みにいく….
# yield文を使ったファイルの読み込み

def read_line_gene(f_path):
  with open(f_path) as f:
    for line in f:
      yield line


for line in read_line_gene('~/large.txt'):
  print(line) ・・・処理

yield from

  • yield from文により、複数のジェネレータ関数を集約して1つのジェネレータ関数にできる
def odd_number_generator():
    for n in range(10):
        if n % 2 == 1:
            yield n
    print('-'*20) # ・・・区切りがわかりやすいように

def even_number_generator():
    for n in range(10):
        if n % 2 == 0:
            yield n

def number_generator():
    yield from odd_number_generator() # ・・・A
    yield from even_number_generator() # ・・・B

for n in number_generator():
    print(n)
最初にAが表示され、終わった後にBが表示される

ループの外側で処理が書ける★

  • 本来、外側のループ(キー)を回し、その内側に処理を書くことが多い
  • 両者を切り離すと、少し見やすいのでは。

ジェネレーター

Pythonのジェネレーターは、イテレータオブジェクトを作成するための一種の関数です。ジェネレーターは、通常の関数と似ていますが、return文の代わりにyield文を使用します。yield文は、関数の実行を一時停止し、値を返します。次に、関数が再度呼び出されたときに、停止した箇所から再開し、値を続けて返します。ジェネレーターは、ループでの使用や、大きなデータセットを扱うときなど、メモリ使用量を削減する場合に特に便利です。ジェネレーターを作成するには、ジェネレーター式や関数定義を使用できます。(ChatGPTより)

# ジェネレーターの作り方は簡単(丸かっこを使う)
code_list = ["0101", "0201", "0301", "0301", "0401", "0501", "0501", "0601", "0601"]
code_gene = (code for code in code_list)

code_gene(next) # 0101
code_gene(next) # 0201
...