このページで解説している内容は、以下の YouTube 動画の解説で見ることができます。

【Python入門】ジェネレータ関数とyield 文

ジェネレータ関数とyield 文

 ジェネレータ関数は、通常の関数とは異なり、一度にすべての値を返すのではなく、必要なときに1個ずつ値を「生成」する関数です。これにより、大量のデータや無限シーケンスを効率的に扱うことができ、メモリ消費を大幅に抑えられます。ここでは、ジェネレータ関数とyield文の基本概念から、より高度なyield from文を使った例まで、具体的なサンプルプログラムとともに解説します。

プログラムのダウンロード

 「ダウンロード」から、JupyterLab で実行できるサンプルプログラムがダウンロードできます。ファイルは、ESET Endpoint Securityでウイルスチェックをしておりますが、ダウンロードとプログラムの実行は自己責任でお願いいたします。

1.ジェネレータ関数とyield文の基本

1.1. ジェネレータ関数とは

 ジェネレータ関数は、関数定義内でreturn文の代わりにyield文を使うことで、呼び出し時にジェネレータオブジェクトを返します。
通常の関数とジェネレータ関数の違いは以下の表のとおりです。

項目通常の関数ジェネレータ関数
戻り値1つの値(またはNone)ジェネレータオブジェクト
値の返し方return文で一度に返す。yield文で必要なときに1個ずつ返す。
メモリ使用全結果を一度に保持する。逐次生成するためメモリ効率が良い。

1.2. yield文の役割

 yield文は、関数の実行を一時停止し、指定した値を返すキーワードです。次回ジェネレータから値を要求されると、yield文の直後から実行が再開されます。これにより、計算結果をその都度生成しながら、処理を続けることが可能になります。

2.ジェネレータ関数の実践例

2.1. 単純なジェネレータ関数の例:立方数の生成

ここでは、1から10までの整数の立方数を順に生成するジェネレータ関数を定義します。

def cube_generator():
    for n in range(1, 11):
        yield n ** 3

# ジェネレータ関数の呼び出しで得られるジェネレータオブジェクトをfor文で反復処理
for cube in cube_generator():
    print(cube, end=' ')
print()  # 改行

実行結果

1 8 27 64 125 216 343 512 729 1000 

解説

  • 関数cube_generator()は、1から10までの整数nについて、nの3乗(n ** 3)をyield文で返します。
  • この関数を呼び出すと、すぐにはすべての値は計算されず、for文で各値が要求されるたびに順次計算されます。
  • 結果として、1, 8, 27, …, 1000といった立方数が順に出力されます。

2.2. yield from文を使ったジェネレータ関数の例:カスタム範囲関数

 yield from文を用いると、別のジェネレータから値をまとめて取り出すことができます。ここでは、開始値から終了値までの整数を生成するカスタム関数 custom_range() を定義し、それを利用して複数回同じ範囲の数値を生成する関数 custom_range_repeat() を作成します。

def custom_range(start, stop):
    x = start
    while x < stop:
        yield x
        x += 1

def custom_range_repeat(start, stop, times):
    # times回、custom_rangeから値をyield fromで取り出す
    for _ in range(times):
        yield from custom_range(start, stop)

# custom_range_repeat関数を使って、3回繰り返し1から5までの整数を生成する
for num in custom_range_repeat(1, 6, 3):
    print(num, end=' ')
print()  # 改行

実行結果

1 2 3 4 5 1 2 3 4 5 1 2 3 4 5 

解説

  • custom_range() 関数は、startからstop-1までの整数をyield文で順に返します。
  • custom_range_repeat() 関数では、for文で指定回数(times)だけループし、その都度yield from文を使ってcustom_range()のすべての値を取り出します。
  • これにより、たとえば start=1, stop=6, times=3 の場合、1から5までの整数が3回連続で生成され、最終的な出力は
      1 2 3 4 5 1 2 3 4 5 1 2 3 4 5
     となります。

まとめ

 ジェネレータ関数とyield文を活用すると、必要なときに値を逐次生成できるため、メモリ効率の良いプログラムを書くことができます。

  • 通常の関数ではreturn文で1度に全結果を返しますが、ジェネレータ関数はyield文を用いて1個ずつ値を返すため、巨大なデータや無限シーケンスの処理に適しています。
  • さらに、yield from文を使えば、既存のジェネレータから効率よく値を取り出し、より複雑な処理をシンプルに記述することが可能です。

 これらの技法を習得することで、柔軟かつ効率的なデータ生成が実現でき、Pythonプログラミングの幅が大きく広がります。