2012/01/29

os.walk()? os.path.walk()???

先日フォルダの中を再帰的に処理する必要があったときに、先輩にこんな愚痴をこぼしました。
Pythonのwalk()ってわかりにくいですよね。」
walk()というのはあるフォルダの中を再帰的に処理するためのメソッドです。
すると先輩は
「え?そう?わかりやすいと思うけど。」

まじですか。。。
俺がバカなだけですか???
と思っていたら、どうやらお互い違うもののことを言っていたらしいです。 

Pythonにはos.walk()とos.path.walk()があるって知ってました?
自分がわかりにくいと言ったのはos.path.walk()。
先輩がわかりやすいと言ったのはos.walk()

os.path.walk()はこんな感じ
C:\Python27\Lib\xmlの中のすべてのファイルを表示する
import os

## dirpathの中のパスをすべて表示する関数
#  os.path.walkの中で使うことで引数に以下の情報が入る
#
#  arg      = walk()の第3引数で指定できる自由になる引数(ここではNone)
#  dirpath  = 現在処理中のディレクトリパス
#  namelist = 現在処理中のディレクトリ内のすべてのアイテムの名前のリスト
#
def printall(arg, dirname, names):
    # 処理中のディレクトリパスとアイテム名を繋げたものがファイルだったら表示
    for name in names:
        fullpath = os.path.join(dirname, name)
        if os.path.isfile(fullpath):
            print fullpath


# ↑のprintall関数を指定してos.path.walk()を実行
os.path.walk(r'C:\Python27\Lib\xml', printall, None)
結果はこんな感じ(要素数:39)
# INFO : C:\Python27\Lib\xml\dom
# INFO : C:\Python27\Lib\xml\etree
# INFO : C:\Python27\Lib\xml\parsers
# INFO : C:\Python27\Lib\xml\sax
# INFO : C:\Python27\Lib\xml\__init__.py
# INFO : C:\Python27\Lib\xml\__init__.pyc
...
これってわかりにくいですよね。。。
walk()に自作の関数を食わせるって。
walk()に食わせた引数が自作関数の中でどう使われるのかも覚えにくいし。
os.walk()はどうかというとこんな感じ
import os

# os.walk()はディレクトリを受け取って中身を
# (処理中のディレクトリパス, その中のディレクトリリスト, ファイルリスト)というタプルを返す
# それをdirpath, dirnames, filenamesに割り振ってfilesの中身をまわしてdirpathと繋げて表示
#
for dirpath, dirnames, filenames in os.walk(r'C:\Python27\Lib\xml'):
    for f in filenames:
        print os.path.join(dirpath, f)
結果はこんな感じ(要素数:39) 順番は異なりますが要素数を見るとどちらもすべてのファイルを検出できてそう。
# INFO : C:\Python27\Lib\xml\__init__.py
# INFO : C:\Python27\Lib\xml\__init__.pyc
# INFO : C:\Python27\Lib\xml\dom\domreg.py
# INFO : C:\Python27\Lib\xml\dom\domreg.pyc
# INFO : C:\Python27\Lib\xml\dom\expatbuilder.py
# INFO : C:\Python27\Lib\xml\dom\minicompat.py
...
うーん、シンプル。。。 こりゃ確かにわかりやすいわ。 今後はこちら一択ですね。 ところでPython2.7のドキュメントを見るとos.path.walk()は廃止予定で3.0ではすでに削除されてるとか。。。 知らなかった。。。 Pythonクイックリファレンスはすごく詳しく書いてあって良いんですが2.3までのことしか書いてないので新しい情報は別から得ないとなぁ。