globでジェネレータ実装を使いたい

 Pythonのモジュールにglobという、ディレクトリリストを取得する機能を提供するものがある。

 とか書くと、Cドライブのルートディレクトリのディレクトリリストが取得できる。

 …で、普段はそんなに必要にならない気もするが、UI的に特にレスポンスを重視する場合、globの返答すら遅いことにイライラさせないようにしたい。なんていう要求もあったりする。
 そういう場合に欲しくなるのが、ジェネレータ実装。

 ジェネレータ実装を知るには、イテレータを知る方が早いと思う。
 イテレータは、検索すると「プログラミング言語において配列やそれに類似する集合的データ構造(コレクションあるいはコンテナ)の各要素に対する繰り返し処理の抽象化」という定義になる…小難しい。
 でも、そこにも書かれているように「配列やそれに類似する集合的データ」これを「各要素に対する繰り返し処理の抽象化」するもの。これがイテレータなので、Python的には「for文のinの後ろにくっ付けられるもの」がイテレータということ。

 イテレータは、配列のように順番に並べられたデータ集合から順番に1個ずつ取り出す機能。
 この「順番に1個ずつ取り出す」というのがポイント。ジェネレータ実装はイテレータの派生のようなもので、「まだ用意されてないデータ集合」がさもあるかのように「順番に1個ずつ取り出す」機能を提供できる。

 for文説明などに出てくるなんてことない処理だけど、このrange関数がジェネレータ。
 [0,1,2,3,4,5,6,7,8,9]と書かないで range(10)と書くことで [0,1,2,3,4,5,6,7,8,9]を生成している range関数。
 簡素に書くとこんな感じ。

 yield i の行は、return i みたいなもの。

yieldは、「yield行を実行した時点で一旦処理をその場で中断し、return文のごとく呼び出し先に値を返す。」という動作をしていて、イテレーションを提供するfor文などの繰り返し処理の中で、次の処理が必要になるとyieldの次行から処理を復帰させる。
なので、イテレーションを繰り返し実行してくれるステートメント内では、次々と処理復帰が行われて1回毎に必要な処理だけを実行することが出来る。
予めすべての要素を作る必要がないので、非常にレスポンスが良い処理を提供できるようになる。

 ジェネレータは、値を1つずつ生成しては yieldで値を返してくる。数千~数万件処理しないと関数から応答が返らないような状況があったとしても、見つけた傍から1つずつ応答を返してくれるので、素早い応答が可能になる。

 話をはじめに戻すと…

 このように指定すると、変数 hoge にジェネレータが生成され、print hoge.next()と実行する度に、値が取得できることが確認できる。

イテレータオブジェクトのnextメソッドは、for文の場合には自動的に呼び出してくれるようになっているので実行している意識はないが、内部的にはnextメソッドが呼ばれている。
英語だが、この記事を読むと詳細を知ることが出来る。

 これを使えば大量のディレクトリリストを処理する場合でも、逐一応答を返せるのでレスポンスの良い実装を書けそう。

そういえば、

 ググってたら「Pythonで再帰的にファイル・ディレクトリを探して出力する」というページを見つけた。
 自前で glob 的な処理を作る場合に参考になるかも。

 さらに、os.walkをググったら、こんなのも。
 「Pythonで、os.walk()を使って、特定のディレクトリを除いたファイル一覧を取得する
 まずos.walkの動きを理解しないとどうにもならないけど、不要な探索を省けるなんて素晴らしい。

気を付けないといけないのは、os.walkの吐き出した dirs への代入の際は、dirs[:] のようにスライスを使った記述をしておかないとNGって部分ですな。
もしくは、dirs.remove(‘削除したい名称’)のように書くか..