プログラムはできるだけCPUやメモリ等のリソース消費量の少ない方法による記述をすることで、システムの障害を未然に防ぐことができます。
Pythonのyield文は、メモリの消費量を抑えて実行できる手法の1つです。
本記事では、Pythonのyield文について解説します。
記事内に記載しているプログラムは、Python3.6.8を使って動作確認をしています。
Pythonのyield文でメモリ使用量の少ないプログラムを作る方法
Pythonのyield文について、以下の内容を解説します。
- yield文とは
- yield文の基本
- yield文を使ったファイルの読み込み
- yield from文
yield文とは
yieldという英単語を辞書で調べると、「生産する」「譲る」「取って代わる」などの意味がある単語となります。
Pythonにおけるyieldを単語の意味に合わせて言うならば、関数の処理を一時的に呼び出し元に譲って、自分は待機しておくという意味になるかと思います。
もう少しプログラム的に説明すると、関数のyield文の場所で処理を一旦停止して戻り値を返し、再度関数が呼ばれると、続きからスタートする、レジューム機能のようなものとなります。
上述の通り、yield文は関数内で使用され、yield文を含む関数をジェネレータ関数と呼びます。
yield文には、関数の戻り値となる値を指定します。
また、関数内にいくつもyield文を入れることができます。
多くの場合はループの処理にて、一旦停止させるような場面に使われることが多いです。
yield文を使うメリットは、大容量のファイルを読み込む場合など、通常の方法では大量のリソースを消費する場面において、一時的に処理を停止することでリソースの消費を抑えることが出来る点にあります。
yield文の基本
yield文の基本的な使い方は以下のようになります。
def func(): yield "Hello" yield "Python"
上記のジェネレータ関数を利用するには、大きく2種類の方法があります。
1つはジェネレータ関数をnextを使って呼び出す方法です。
f1 = func() s = next(f1) print(s) // -> Hello s = next(f1) print(s) // -> Python
もう1つは、ループ(fon-in)を使って呼び出す方法です。
f2 = func() for s in f2: print(s) // -> Hello -> Python
ループで利用する場合は、yieldの数分だけループします。
nextで呼び出す場合は、yieldの数を理解して呼び出す必要があり、yield文の数を超えて呼び出すと例外が発生します。
また、以下のようにして、ジェネレータ関数のすべての結果をリストに格納することもできます。
f3 = func() print(list(f3)) // -> ['Hello', 'Python']
yield文を使ったファイルの読み込み
yield文の使い所は、大量のリソース消費を必要とする場面です。
その1つとなる大きなサイズのファイルを読み込む際に、yield文を使って1行ずつ停止しながら処理を行うことで、リソース消費量を1行分に抑えることができます。
def read_line_generator(filepath): with open(filepath) as file: for line in file: yield line for line in read_line_generator("./large_size_file.txt"): print(line)
上記のジェネレータ関数では、yield文で1行分のデータを返しています。
そのジェネレータ関数を呼び出し側がループで呼び出すことで、1行分のデータを取得しながら処理を行うことができます。
yield from文
yield from文により、いくつものジェネレータ関数を、集約した1つのジェネレータ関数とすることができます。
def odd_number_generator(): for n in range(10): if n % 2 == 1: yield n def even_number_generator(): for n in range(10): if n % 2 == 0: yield n def number_generator(): yield from odd_number_generator() yield from even_number_generator() for n in number_generator(): print(n)
上記のプログラムでは、奇数を返すジェネレータ関数odd_number_generatorと偶数を返すeven_number_generatorがあり、それらをyield fromによって集約したnumber_generator関数を定義しています。
集約した関数もジェネレータ関数として、ループで使用することができます。
実行結果は以下のようになります。
1 3 5 7 9 0 2 4 6 8
通常のジェネレータ関数は上から順に実行されていくので、集約されても順番通りに実行されます。
ジェネレータ関数からリストを作成しても、順番は同じになります。
print(list(number_generator())) // -> [1, 3, 5, 7, 9, 0, 2, 4, 6, 8]
まとめ
Pythonのyield文についてまとめると、以下となります。
- yieldとは、関数の処理を一時的に呼び出し元に譲って(戻して)、自分は待機しておくレジューム機能のようなものである。
- yield文を使うことで、メモリを大量に使うループなどにおいて、リソース消費量を抑えることができる。
- yield from文を使うことで、ジェネレータ関数を集約することができる。
リソースの消費量をできるだけ小さくするのは、プログラマの腕の見せどころです。
大量のデータを扱う際は、yield文を使ってみてはいかがでしょうか。
今回はPythonのyield文について解説しました。
以上、参考になれば幸いです。
コメント