HOWTO · Python

Python ジェネレーター クラス

Python でジェネレーター クラスを作成するには、yield ステートメントと next() 関数を使用できます。

このページの内容

このチュートリアルでは、yield ステートメントと next() 関数を使用して Python でジェネレーター クラスを作成する方法について説明します。

ジェネレーターを理解するには、まず以下で説明するイテレーターを理解する必要があります。

Python イテレータ

イテレータは、コンテナ内の要素に 1つずつアクセスするために使用されるオブジェクトです。 for ステートメントを使用してコンテナ オブジェクトをループし、値を個別に取得できます。

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

for element in [5, 6, 7]:
    print(element)

上記の Python コードでは、要素のリストをループして 1つずつ出力しています。 舞台裏で何が起こっているのかを理解しましょう。

for ステートメントは、指定されたコンテナー オブジェクトに対して iter() 関数を呼び出します。この関数には、指定されたコンテナー オブジェクトの各要素に 1つずつアクセスするメソッド __next__() が含まれています。

__next__() 関数が例外 StopIteration を発生させると、ループは終了します。この例外は、指定されたコンテナー オブジェクト内にそれ以上要素が存在しない場合にのみ発生します。

Python は、__next__() 関数を呼び出すために使用できる組み込み関数 next() も提供します。 コンテナー オブジェクトで next() 関数を使用するには、iter() 関数を使用してオブジェクトを作成する必要があります。

たとえば、数値のリストを使用して next() 関数を呼び出して、リストの各要素を 1つずつ取得してみましょう。 以下のコードと出力を参照してください。

My_list = [5, 6, 7]
iter_object = iter(My_list)
print(next(iter_object))
print(next(iter_object))
print(next(iter_object))
print(next(iter_object))

出力:

5
6
7
---------------------------------------------------------------------------
StopIteration                             Traceback (most recent call last)
<ipython-input-10-aa02bcda701b> in <module>
      4 print(next(iter_object))
      5 print(next(iter_object))
----> 6 print(next(iter_object))

StopIteration:

上記のコードでは、next() 関数を 4 回呼び出し、指定されたリスト オブジェクトの 3つの要素を個別に返しました。 リストオブジェクトにそれ以上要素が存在しないため、4回目に呼び出したときにStopIteration例外が返されました。

ループを使用して next() 関数を呼び出すこともできます。

try-except ステートメントを使用すると、エラーを回避し、例外名を使用してループを終了できます。 たとえば、ループと try-except ステートメントを使用して上記のコードを繰り返してみましょう。

以下のコードと出力を参照してください。

My_list = [5, 6, 7]
iter_object = iter(My_list)

for i in range(len(My_list)):
    try:
        print(next(iter_object))
    except StopIteration:
        break

出力:

5
6
7

上記のコードでは、例外の名前 StopIteration を使用してループを中断しました。 上記の反復子は順方向に 1つずつ値を返しますが、要件に応じて値を返す独自の反復子を定義することもできます。

クラスに反復子の動作を追加するには、__init__()、__iter__()、および __next__() の 3つの関数を定義する必要があります。 たとえば、フィボナッチ数 を返すクラスを作成してみましょう。

以下のコードを参照してください

class Fibexample:
    def __init__(self):
        self.x, self.y = 0, 1

    def __iter__(self):
        return self

    def __next__(self):
        r_value = self.x
        self.x, self.y = self.y, self.x + self.y
        return r_value


fib = Fibexample()
for i in range(7):
    print(next(fib))

出力:

0
1
1
2
3
5
8

上記のクラスは、next() 関数が呼び出されるたびに、フィボナッチ数列から数値を返します。 上記のコードでは、next() 関数を 7 回呼び出して、フィボナッチ数列の最初の 7つの数値を返しています。

Python ジェネレーター クラス

Python では、ジェネレーターを使用してイテレーターを作成します。 それらは通常の関数と同じです。 唯一の違いは、return ステートメントの代わりに yield ステートメントを使用することです。

yield() ステートメントは、反復子オブジェクトを返す next() 関数を呼び出します。 たとえば、上記のコードと同じフィボナッチ数列を返すジェネレータ関数を作成してみましょう。

以下のコードと出力を参照してください。

def fibexample(data_input):
    x, y = 0, 1
    for index in range(data_input):
        z = x
        x, y = y, x + y
        yield z


obj = fibexample(7)
for i in obj:
    print(i)

出力:

0
1
1
2
3
5
8

上記のコードでは、fibexample() 関数は必要なフィボナッチ数列をイテレータ オブジェクトで返します。 ループを使用してオブジェクトを反復処理し、反復子オブジェクトに存在する各値を取得できます。

ジェネレーターは、データ値と next() 関数の最後の実行を記憶しており、next() 関数が再度呼び出されると中断したところから再開します。

上記の関数の結果は、反復子の例で取得したものと同じですが、上記のコードは、反復子の例で使用したコードと比較して比較的短いです。 ジェネレーターを使用する利点は、__iter__() および __next__() 関数が自動的に作成され、ジェネレーターが StopIteration 例外も処理することです。

したがって、ジェネレーターを使用してイテレーターを作成することは、yield ステートメントを使用して単純な関数を作成することに似ているため、ジェネレーターを使用してイテレーターを作成するのは簡単です。