特定のパスにあるすべてのファイルに何かをしようとしています。すべてのファイル名を事前に収集してからそれらを使用する必要はないので、これを試しました。
import os
import stat
def explore(p):
s = ''
list = os.listdir(p)
for a in list:
path = p + '/' + a
stat_info = os.lstat(path )
if stat.S_ISDIR(stat_info.st_mode):
explore(path)
else:
yield path
if __name__ == "__main__":
for x in explore('.'):
print '-->', x
しかし、このコードは、ディレクトリにヒットすると、コンテンツを生成するのではなく、ディレクトリをスキップします。何が悪いのですか?
使用する - os.walk
ホイールを再発明する代わりに。
特に、ライブラリのドキュメントの例に従って、テストされていない試みを次に示します。
import os
from os.path import join
def hellothere(somepath):
for root, dirs, files in os.walk(somepath):
for curfile in files:
yield join(root, curfile)
# call and get full list of results:
allfiles = [ x for x in hellothere("...") ]
# iterate over results lazily:
for x in hellothere("..."):
print x
イテレータはそのように再帰的に機能しません。置き換えることにより、各結果を再生成する必要があります
_explore(path)
_
のようなもので
_for value in explore(path):
yield value
_
Python 3.PEP 38 で提案されている構文_yield from X
_を追加して、この目的を果たします。代わりにこれを行うことができます:
_yield from explore(path)
_
generators as coroutines を使用している場合、この構文は generator.send()
の使用もサポートしており、再帰的に呼び出されるジェネレーターに値を返します。上記の単純なfor
ループはそうではありません。
問題は次のコード行です。
_explore(path)
_
それは何をするためのものか?
explore
でpath
を呼び出しますexplore
実行、ジェネレーターの作成explore(path)
が実行された場所に戻ります。。。なぜ捨てられるのですか?何にも割り当てられておらず、繰り返しも行われていません。完全に無視されました。
結果で何かをしたいのなら、まあ、あなたはそれらで何かをしなければならない! ;)
コードを修正する最も簡単な方法は次のとおりです。
_for name in explore(path):
yield name
_
何が起こっているかを理解していると確信できる場合は、代わりにos.walk()
を使用することをお勧めします。
Python 3.3(すべてが計画どおりに機能すると想定)に移行すると、新しい_yield from
_構文を使用できるようになり、その時点でコードを修正する最も簡単な方法はになる:
_yield from explore(path)
_
これを変える:
explore(path)
これに:
for subpath in explore(path):
yield subpath
またはos.walk
、phoojiが示唆するように(これはより良いオプションです)。
関数のようにexplore
を呼び出します。あなたがすべきことはジェネレータのようにそれを繰り返すことです:
_if stat.S_ISDIR(stat_info.st_mode):
for p in explore(path):
yield p
else:
yield path
_
編集:stat
モジュールの代わりに、os.path.isdir(path)
を使用できます。
これを試して:
if stat.S_ISDIR(stat_info.st_mode):
for p in explore(path):
yield p
スタックを使用して再帰を実装することもできます。
これを行うことには、それが可能であるという事実以外に、何の利点もありません。最初にpython=を使用している場合、パフォーマンスの向上はおそらく価値がありません。
import os
import stat
def explore(p):
'''
perform a depth first search and yield the path elements in dfs order
-implement the recursion using a stack because a python can't yield within a nested function call
'''
list_t=type(list())
st=[[p,0]]
while len(st)>0:
x=st[-1][0]
print x
i=st[-1][1]
if type(x)==list_t:
if i>=len(x):
st.pop(-1)
else:
st[-1][1]+=1
st.append([x[i],0])
else:
st.pop(-1)
stat_info = os.lstat(x)
if stat.S_ISDIR(stat_info.st_mode):
st.append([['%s/%s'%(x,a) for a in os.listdir(x)],0])
else:
yield x
print list(explore('.'))
os.walkは、すべてのフォルダーとサブフォルダーを走査する必要がある場合に最適です。それが必要なければ、象銃を使ってハエを殺すようなものです。
ただし、この特定のケースでは、os.walkの方が優れたアプローチである可能性があります。
元の質問に答えるには、重要なのは、yield
ステートメントを再帰から伝達し直す必要があることです(たとえば、return
のように)。以下はos.walk()
の再実装です。私はこれを疑似VFS実装で使用しています。ここで、os.listdir()
および同様の呼び出しをさらに置き換えます。
import os, os.path
def walk (top, topdown=False):
items = ([], [])
for name in os.listdir(top):
isdir = os.path.isdir(os.path.join(top, name))
items[isdir].append(name)
result = (top, items[True], items[False])
if topdown:
yield result
for folder in items[True]:
for item in walk(os.path.join(top, folder), topdown=topdown):
yield item
if not topdown:
yield result