web-dev-qa-db-ja.com

Pythonでファイルを再帰的に検索する最も速い方法は何ですか?

再帰的に検索して、特定の文字列を含むパスを含むファイルのリストを生成する必要があります。私は現在これを次のようにしています:

for i in iglob(starting_directory+'/**/*', recursive=True):
    if filemask in i.split('\\')[-1]: # ignore directories that contain the filemask
        filelist.append(i) 

これは機能しますが、大きなディレクトリツリーをクロールする場合、非常に遅くなります(約10分)。私たちはWindowsを使用しているので、unix findコマンドを外部呼び出しすることはオプションではありません。私の理解では、グロブはos.walkよりも高速です。

これを行うより速い方法はありますか?

6

多分あなたが望んでいた答えではないかもしれませんが、これらのタイミングは有用だと思います。合計102,799ファイル(そのうち3059は.pyファイル)の15,424個のディレクトリがあるディレクトリで実行します。

Python 3.6:

import os
import glob

def walk():
    pys = []
    for p, d, f in os.walk('.'):
        for file in f:
            if file.endswith('.py'):
                pys.append(file)
    return pys

def iglob():
    pys = []
    for file in glob.iglob('**/*', recursive=True):
        if file.endswith('.py'):
            pys.append(file)
    return pys

def iglob2():
    pys = []
    for file in glob.iglob('**/*.py', recursive=True):
        pys.append(file)
    return pys

# I also tried pathlib.Path.glob but it was slow and error prone, sadly

%timeit walk()
3.95 s ± 13 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

%timeit iglob()
5.01 s ± 19.2 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

%timeit iglob2()
4.36 s ± 34 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

Cygwin(4.6.0-1)でGNU find(4.6.0)を使用する

$ time find . -name '*.py' > /dev/null

real    0m8.827s
user    0m1.482s
sys     0m7.284s

のように思える os.walkはあなたが得ることができるのと同じくらい良いです。

11
FHTMitchell