1. ホーム
  2. パイソン

[解決済み】ジェネレーター関数は何に使えるの?

2022-04-22 10:06:07

質問

Pythonを学び始めて、ジェネレータ関数(yield文があるもの)に出会いました。 これらの関数がどのような種類の問題を解決するのが得意なのか知りたいです。

どのように解決するのですか?

ジェネレータは遅延評価を可能にします。for'を使って明示的に、あるいは反復処理を行う関数や構成体に暗黙的に渡して、それらを反復処理することで使用します。ジェネレータはリストを返すように複数の項目を返すと考えることができますが、一度にすべてを返すのではなく、1つずつ返し、次の項目が要求されるまでジェネレータ関数は一時停止されます。

ジェネレーターは、大きな結果のセット(特にループを含む計算)を計算する際に、すべての結果が必要になるかどうかわからない場合や、すべての結果に対して同時にメモリを割り当てたくない場合に適しています。あるいは、ジェネレータが もう一つ ジェネレータを使用したり、他のリソースを消費したりするため、できるだけ遅く発生した方が便利です。

ジェネレータのもうひとつの用途は、コールバックをイテレーションに置き換えることです(これは本当に同じです)。ある関数に多くの作業をさせ、時折呼び出し元に報告させたい場合があります。従来は、このためにコールバック関数を使用していました。このコールバック関数をワーク関数に渡すと、ワーク関数は定期的にこのコールバックを呼び出す。ジェネレータのアプローチでは、ワークファンクション(現在はジェネレータ)はコールバックについて何も知らないので、何かを報告したいときに呼び出すだけである。呼び出し側は、別のコールバックを書いてワークファンクションに渡す代わりに、ジェネレータの周りの小さな「for」ループですべての報告作業を行います。

例えば、「ファイルシステム検索」プログラムを書いたとします。検索を全体的に行い、結果を収集し、一度に一つずつ表示することができます。すべての結果は、最初の結果を表示する前に収集されなければならず、すべての結果は同時にメモリ内に存在することになります。あるいは、結果を見つけながら表示することもできます。この方がメモリ効率が良く、ユーザーにも親切です。後者は、ファイルシステム検索関数に結果表示関数を渡すことで実現できますし、検索関数をジェネレータにしてその結果を繰り返し表示するだけでも実現できます。

後者の2つのアプローチの例を見たい場合は、 os.path.walk() (コールバック付きの古いファイルシステムウォーキング関数) と os.walk() (新しいファイルシステムウォーキングジェネレータ) を見てください。もちろん、本当にすべての結果をリストに集めたい場合は、ジェネレータのアプローチをビッグリストのアプローチに変換するのは些細なことです。

big_list = list(the_generator)