この内容は古いバージョンです。最新バージョンを表示するには、戻るボタンを押してください。
バージョン:4
ページ更新者:guest
更新日時:2026-06-11 07:10:02

タイトル: yieldについて
SEOタイトル: Python yield 完全ガイド — ジェネレータの仕組みと活用

この記事の要点
  • yield を含む関数は ジェネレータ関数になり、呼び出すと即座に ジェネレータオブジェクトを返す
  • next(gen) で次の yield まで実行 → 値を返して関数の実行を一時停止、再度 next で続きから再開
  • メモリ効率: 無限数列・ファイル行・DB クエリ等を 1 要素ずつ生成するのでリストより圧倒的に省メモリ
  • yield from iterable で別のイテラブルに委譲、内部状態も中継
  • ジェネレータ式 (x*2 for x in range(10)) はリスト内包の括弧違い版、巨大データに有効
  • コルーチンとしての側面: gen.send(value) で yield 式に値を注入できる
  • 非同期ジェネレータ async def + yieldasync for でストリーム処理

yield とは何か

Python の yield は関数をジェネレータ関数に変える文です。通常の関数は呼ぶと最後まで実行して戻り値を返しますが、yield を含む関数は呼んでも本体を実行せず、ジェネレータオブジェクトを返します。next()for ループで反復するたびに、yield 文まで実行 → 値を返して一時停止 → 次の next で続きから再開、を繰り返します。

def counter():
    print("start")
    yield 1
    print("middle")
    yield 2
    print("end")
    yield 3

g = counter()        # 関数本体はまだ実行されない
print(type(g))       # <class 'generator'>

print(next(g))       # → start  1
print(next(g))       # → middle 2
print(next(g))       # → end    3
print(next(g))       # → StopIteration 例外

return vs yield

項目returnyield
関数の実行呼出時に即実行呼出時はオブジェクトのみ生成
戻り値1 つの値反復ごとに 1 つずつ無限/有限の値列
関数終了return で完全終了yield 後も停止状態で次の呼出を待つ
状態保持しないローカル変数を保持
メモリ戻り値分1 要素分のみ (省メモリ)

メモリ効率: 無限数列 / 大量データ

# ✗ 1 億要素のリスト → メモリを大量消費
def squares_list(n):
    return [i * i for i in range(n)]

# ✓ ジェネレータ → 1 要素分のメモリのみ
def squares_gen(n):
    for i in range(n):
        yield i * i

# 利用例 (両方とも同じ for で書ける)
for x in squares_gen(100_000_000):
    if x > 10_000:
        break

# 無限数列も書ける (リストでは不可能)
def naturals():
    n = 0
    while True:
        n += 1
        yield n

import itertools
first_10 = list(itertools.islice(naturals(), 10))
print(first_10)  # [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

yield from: ジェネレータ委譲

def inner():
    yield 1
    yield 2
    yield 3

def outer():
    yield 0
    yield from inner()    # inner の値を全てそのまま渡す
    yield 4

print(list(outer()))      # [0, 1, 2, 3, 4]

# 同等の冗長な書き方
def outer_long():
    yield 0
    for v in inner():
        yield v
    yield 4

# yield from は値だけでなく send() / throw() / return 値も中継する点で
# 単純な for-yield より強力

ジェネレータ式

# リスト内包 (角括弧) → リストを即座に作る
xs = [x * 2 for x in range(1_000_000)]   # 全要素をメモリに展開

# ジェネレータ式 (丸括弧) → ジェネレータを返す
gen = (x * 2 for x in range(1_000_000))  # 1 要素分のみ
total = sum(gen)                          # 反復しながら計算

# 関数の引数として渡すと括弧省略可能
total = sum(x * 2 for x in range(1_000_000))
max_val = max(len(line) for line in open("data.txt"))

コルーチンとしての yield (send / throw)

yield はでもあります。gen.send(value) でジェネレータに値を送り込めます:

def echo():
    while True:
        received = yield   # ここで止まる、send で値が来る
        print(f"got: {received}")

g = echo()
next(g)            # ジェネレータを yield 位置まで進める (priming)
g.send("hello")    # → got: hello
g.send("world")    # → got: world

# 累積平均コルーチン
def averager():
    total = 0
    count = 0
    avg = None
    while True:
        value = yield avg
        total += value
        count += 1
        avg = total / count

a = averager()
next(a)
print(a.send(10))  # 10.0
print(a.send(20))  # 15.0
print(a.send(30))  # 20.0

この機構は asyncio 登場前 (Python 3.4 以前) の非同期処理の基礎でした。現在は async/await の方が読みやすいため、コルーチン目的では yield をあまり使いません。

非同期ジェネレータ (Python 3.6+)

import asyncio

async def fetch_pages(urls):
    for url in urls:
        # async I/O で取得
        await asyncio.sleep(0.1)
        yield f"page of {url}"

async def main():
    urls = ["a", "b", "c"]
    async for page in fetch_pages(urls):
        print(page)

asyncio.run(main())

他言語の yield との比較

言語構文特徴
Pythonyield value関数全体がジェネレータ化、双方向通信可
C# / Unityyield return value;IEnumerable を返す、コルーチンにも
JavaScriptfunction* gen() { yield value; }function* 構文で明示、Iterator プロトコル
Rubyyield valueブロックへ値を渡す (Python と意味が異なる)
Kotlinyield(value) in sequencesequence ビルダ内のみ

典型的なユースケース

  • ファイル行を 1 行ずつ処理: for line in open("huge.csv") 自体がジェネレータ
  • DB クエリのストリーミング: cursor をジェネレータでラップ
  • ツリー走査: 再帰的に yield from で深さ優先列挙
  • パイプライン処理: gen1 → gen2 → gen3 の連鎖で省メモリな ETL
  • ページネーション: API の next_page を辿ってフラットに列挙

FAQ

Q: ジェネレータは何回でもループできる?
A: 1 回のみ。使い切ったら再度生成し直す必要があります。複数回反復したいなら list 化するか、iterable クラス (__iter__ 実装) にします。

Q: return value をジェネレータ内で使うと?
A: Python 3.3+ では StopIteration(value) を発生させます。yield from で値を受け取れます。

Q: yield と await の関係
A: 内部実装的には親戚 (どちらもコルーチン)。await は asyncio の文脈、yield はジェネレータの文脈、と使い分けます。

Q: ジェネレータをデバッグしにくい
A: 一度 list(gen) で展開してから print するのが基本。または printyield 直前に挟む。