2011-07-20 20 views
50

私は与えられたパスの下のすべてのファイルに対して何かをしようとしています。私は、事前にすべてのファイル名を収集し、それらに何かをしたくないので、私はこの試みた:再帰関数の収穫

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 

をしかし、それは代わりにその内容を生み出すのは、それらをヒットしたときに、このコードは、ディレクトリをスキップ。私は間違って何をしていますか?

+0

一部の言語では、個々の要素だけでなく、シーケンス全体が得られます。私はPythonもその一つだとは思わない。 http://www.mindscapehq.com/blog/index.php/2011/02/28/recursive-iterators-in-f/ – Leonid

+0

タイトルはos.walkで解決できるよりも一般的な問題を示唆しているので、これを考慮してください: DEF(P)探検:Pにおけるxの :(P、(リスト、タプル))でisinstance場合 を は他 (p)を探る: 利回りのp は、これは、同じ問題を抱えています。それはなぜ機能しないのですか? – JimB

答えて

26

車輪を改造する代わりに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 
+13

作業コードを与えるのは良いですが、OPが間違っていたことを説明すると、特に彼らがそれを求めるときには、さらに優れています。 –

+1

質問は歩留まりと再帰に関するものであり、os.walk – Massimo

+0

を実装する最良の方法ではありません。Python 2でwalkが遅い場合はlistdir、https://www.python.org/dev/peps/pep-0471/を参照してください。 – Massimo

2

はこれを試してみてください:関数のようexploreを呼び出し

if stat.S_ISDIR(stat_info.st_mode): 
    for p in explore(path): 
     yield p 
3

特に

は、ライブラリのドキュメントの例以下、ここではテストされていない試みです。

if stat.S_ISDIR(stat_info.st_mode): 
    for p in explore(path): 
    yield p 
else: 
    yield path 

EDIT::代わりstatモジュールの、あなたがos.path.isdir(path)を使用することができますあなたがすべきことは発電機のようにそれを反復です。

123

反復子はそのように再帰的には動作しません。あなたは、この目的を果たすために、PEP 380に提案されているように、構文yield from Xを追加

for value in explore(path): 
    yield value 

Python 3.3のようなもので

explore(path) 

を交換することにより、それぞれの結果を再生成する必要があります。それを使用すると、代わりにこれを行うことができます。

yield from explore(path) 

あなたがgenerators as coroutinesを使用している場合は、この構文はまた、再帰的に呼び出さ発電機に戻す値を渡すgenerator.send()の使用をサポートしています。上の単純なforループはそうではありません。

+13

+1は3.3のフィーチャーに言及しています。私はこれまで知らなかった:) – phooji

+17

これは受け入れられた答えであるはずです。質問は収量と再帰に関するものであり、os.walkを実装する最良の方法ではありません。 !私はこの非常に単純なループで私の頭を壊していた...そして、実際には他のすべての答えは同じ行にあります... – Stefano

+2

PEPへのリンクは、これをもっと詳しく説明しています。 – mpontillo

8

変更この:これに

explore(path) 

:phoojiが提案されているよう

for subpath in explore(path): 
    yield subpath 

または(より良いオプションである)、os.walkを使用しています。

35

問題は、このコード行です:

explore(path) 

それは何をしますか?

  • は、発電機
  • ジェネレータはexplore(path)を実行されたスポットに返されるを作成し、新しいpath
  • exploreの実行とexplore呼び出します。 。 。
  • 、それが破棄されるのはなぜ

が破棄されますか?それは何にも割り当てられておらず、反復されず、完全に無視されました。

あなたが結果で何かをしたいのであれば、あなたはそれらと何かをしなければなりません! ;)

あなたのコードを修正する最も簡単な方法は次のとおりです。

for name in explore(path): 
    yield name 

あなたは何が起こっているか理解して自信を持っているとき、あなたはおそらく代わりにos.walk()を使用したいと思います。あなたは(計画通り全てがうまくいくと仮定した場合)はPython 3.3に移行した後

新しいyield from構文を使用することができるようになりますと、その時点で、あなたのコードを修正する最も簡単な方法は次のようになります。

yield from explore(path) 
+6

元のポスターのコードがうまくいかなかった理由の良い説明。 –

0

os.walkは、すべてのフォルダとサブフォルダをトラバースする必要がある場合に最適です。あなたがそれを必要としないなら、ゾウを殺すために象の銃を使うようなものです。

しかし、この特定のケースでは、os.walkがより良いアプローチになる可能性があります。

0

また、スタックを使用して再帰を実装することもできます。

実際に可能であることを除いて、これを行うにあたって実際に利点はありません。あなたが最初に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('.'))