Pythonにおける"yield"とは何か?
この記事の目次
Pythonにおけるyield
キーワードについて、この記事で詳しく解説します。yield
が提供する機能、その使用方法、およびコード例をもとにどのような役割を果たしているか見ていきます。
1. yield
とは何か?
Pythonでyield
キーワードは、関数の実行を一時停止して値を返し、関数の状態を保持したまま次の呼び出しで再開するために使われます。これにより、その関数は「ジェネレーター関数」として動作し、イテレーターを返すようになります。通常のreturn
と異なり、yield
を使うと関数は中断と再開が可能となり、メモリ効率が向上します。
2. イテラブルとイテレーター
Pythonのfor...in...
構文で反復できるオブジェクトはすべて「イテラブル」です。リスト、文字列、ファイルなどが含まれます。イテラブルは必要に応じてメモリにデータをすべて保持し、何度でもアクセスが可能です。
一方、「イテレーター」は一度きりの読み取り専用のオブジェクトです。値をその場で生成しながら、次に進むと前の値を保持しないため、特に大量データを扱う際にメモリの節約が可能です。
3. ジェネレーターとは?
ジェネレーターは、イテレーターの一種です。関数内でyield
が使用されると、その関数はジェネレーター関数になり、呼び出し時にジェネレーターオブジェクトを返します。ジェネレーターは値を逐次生成するため、メモリ効率が良く、大量のデータを扱う際に適しています。
例:
def create_generator(): for i in range(3): yield i * i
この関数はジェネレーターを返し、ループを通じて0、1、4を生成します。yield
があるため、ループごとに実行が中断・再開され、次の値が計算されるまで関数の状態が保持されます。
4. yield
の仕組み
関数にyield
があると、関数を呼び出した際に即座にコードが実行されるわけではありません。まずはジェネレーターオブジェクトが返され、for
ループなどでそのオブジェクトが利用されると初めて、yield
に到達するまでのコードが実行されます。
このとき、yield
に到達するとその時点の値を返し、次にそのジェネレーターオブジェクトが呼び出されるまで関数は中断状態になります。呼び出しが続く限り、ジェネレーターは新しい値を順次生成しますが、ループが終了またはif
条件によりyield
が呼ばれなくなると、ジェネレーターは空と見なされ終了します。
5. yield
を使った具体例:子候補を取得する関数
以下はyield
を利用した例です。このコードはツリー構造のデータを扱っており、条件に応じて子ノードを返します。
def _get_child_candidates(self, distance, min_dist, max_dist): if self._leftchild and distance - max_dist < self._median: yield self._leftchild if self._rightchild and distance + max_dist >= self._median: yield self._rightchild
この関数は条件に基づいて左右の子ノードをyield
し、結果をジェネレーターとして返します。呼び出し側ではこのジェネレーターを使い、子ノードを順次取得して処理を進めることができます。
呼び出し側コード例:
result, candidates = [], [self] while candidates: node = candidates.pop() distance = node._get_dist(obj) if min_dist <= distance <= max_dist: result.extend(node._values) candidates.extend(node._get_child_candidates(distance, min_dist, max_dist))
このようにyield
を使うと、一度にすべての子ノードをリストとして返さずに順次返し、メモリ使用量を削減できます。また、extend
メソッドはリストだけでなくジェネレーターも受け付けるため、効率的に次の子ノードを処理できます。
6. 応用:ジェネレーターの制御
以下のように、yield
を使ってリソースへのアクセスを制御することも可能です。
class Bank(): crisis = False def create_atm(self): while not self.crisis: yield "$100"
この例では、ATMから現金を引き出す際にcrisis
フラグによって操作を制限できます。
まとめ
yield
を活用すると、ジェネレーターを通じて大量のデータを効率よく扱うことが可能になります。イテラブルとしてのyield
の利点を理解することで、Pythonのメモリ効率をさらに高めることができます。特にツリー構造などの再帰的な処理や、逐次生成が必要な場合に役立ちます。