この連載では、Pythonについて色々な形で再学習に取り組んでいます。前回の記事はこちらになります。
前回は、イテレータについて学びました。イテラブル、イテレータの仕組みがわかったと思います。
今回は、ジェネレーターです。イテレータ同様繰り返しのデータを扱う仕組みのようですが、どのようなものなのでしょうか。
ジェネレーターとは
ジェネレーターとは、必要になったときに次のデータを生成する仕組みです。以下は前回ご紹介したリストからイテレータを生成して処理する例です。
it = iter([0,1,2]) # イテラブル(リスト)からイテレータを生成 print(next(it)) # 0 を返す print(next(it)) # 1 を返す print(next(it)) # 2 を返す print(next(it)) # 例外 StopIteration が発生する
この例では、処理に必要なデータ(リスト)は、あらかじめ用意されています。上の例ではデータ量は少ないのですが、データ量が膨大になると、そのデータがメモリを占有してしまいます。また、データを用意するために複雑な計算を要する場合などでは、データの用意に時間がかかって、処理の開始が遅れてしまったりします。
このような時にジェネレーターを使用すると、効率のよいプログラムが書けます。以下は上記の例をジェネレーターで書き換えたものです。
def generate_test_1(end): for i in range(0, end): yield i ge = generate_test_1(3) print(next(ge)) # 0 を返す print(next(ge)) # 1 を返す print(next(ge)) # 2 を返す print(next(ge)) # 例外 StopIteration が発生する
これを「ジェネレーター関数」といいます。通常の関数は、returnで値を返して終了となりますが、ジェネレーター関数ではyieldで値を返します。yieldが呼ばれると関数の実行は一時停止し、次に呼び出されたときにその次の場所から再開します。 これならばデータ量が多い場合でも、データがメモリを占有することはありません。データ型はジェネレーター型です。
print(type(ge)) # <class 'generator'> を返す
また、ジェネレーターは特殊メソッド「iterメソッド」、「nextメソッド」を持っています。ジェネレーターは、イテレータの一種です。
先の例ではデータ数を指定していましたが、データ数を指定せずに(無限に)値を返すジェネレーター関数も記述可能です。
def generate_numbers(): i = 0 while(True): yield i i += 1 ge = generate_numbers() print(next(ge)) # 0 を返す print(next(ge)) # 1 を返す ・ ・
この例ではyieldで値を返した後、次の呼び出しでは次の文から再開します。関数の場合はreturnの後に記述された内容は無効になりますが、yieldでは有効になります。
ジェネレーター式
内包表記と似た様な記法で、ジェネレーターを生成する方法もあります。以下は、1~5の整数のうち奇数のみのリストを返すリスト内包表記です。
[i for i in range(1,6) if i % 2]
この値を順に返すジェネレーターは、以下のように記述できます。
ge = (i for i in range(1,6) if i % 2) print(next(ge)) # 1 を返す print(next(ge)) # 3 を返す print(next(ge)) # 5 を返す print(next(ge)) # 例外 StopIteration が発生する
リスト内包表記との違いは、"[]"ではなく"()"で括る点です。
無限に続くジェネレーター式
データ数を指定せずに(無限に)値を返すジェネレーター式は、「itertoolsモジュール」をインポートすることで可能になります。countオブジェクトを使用して、引数には開始する数値を指定します。引数を省略すると0になります。
import itertools ge = (i for i in itertools.count(3)) print(next(ge)) # 3 を返す print(next(ge)) # 4 を返す print(next(ge)) # 5 を返す ・ ・
次回はデコレーター
いかがでしょうか。ジェネレーターを使うと、効率よく繰り返しのデータを作成できます。次回はデコレーターです。関数やメソッド、クラスの定義の前に記述するのですが、どのような機能があるのでしょうか。お楽しみに!!