🫥

Pythonのyieldの動きと使い方について

に公開

はじめに

pythonコードを書いている時に、yieldの動きと使い所について調べたので本記事でまとめます。

yieldとは?

  • returnと似ているが、ジェネレーターと呼ばれる特殊なイテレータが生成される
    • イテレータとは、リストなどデータを一つずつ順番に取り出すオブジェクト
    • リストはメモリ上に全ての要素を保持する一方で、ジェネレーターは必要に応じて値を一つずつ保持する
    • yieldを含む関数を「ジェネレーター関数」とも呼ぶ
  • yieldが実行されるたびに関数は一時停止、再度関数を呼び出すと続きから実行する

以下にコード例を示します。

# ジェネレーター関数を定義
def generate_numbers():
    for i in range(1,4):
        yield i

# 関数を呼び出すだけでは、ジェネレーターオブジェクトが生成されるのみ
numbers = generate_numbers()
print(numbers) # <generator object generate_numbers at 0x00000000e0>

# next()を使い、値を一つずつ取得することもできる
print(next(numbers))  # 1
print(next(numbers))  # 2

何が嬉しいのか?

上記だけ見ると「returnやlistで同じようなことができるじゃん」という感じですが、
yieldの良さももちろんあります。

それは、 「リストに比べると少ないメモリ量で処理を実行することができる」 点です。

前述した通り、リストの場合は全データを保持するメモリ領域を確保する挙動になります。例えば100万個の要素が入っているリストを宣言した場合、100万個分の要素が入るメモリ領域を確保します。

一方で、yieldは今の関数の状態だけメモリに保持し、必要な時に要素を取得するためメモリが小さくて済みます。

若干脱線:pytestにおけるyield

個人的にはpythonのテストフレームワークの一つであるpytestyieldをよく見かけます。

test関数を実行する前のsetup関数内で、テスト実行前の処理とテスト実行後の処理の間にyieldを入れることで、テスト実行前の処理を実行 -> yield -> テスト実行後の処理を実行

pytestにおけるyieldのドキュメント

Discussion