Python 递归函数的屈服
我正在尝试对给定路径下的所有文件执行某些操作。我不想事先收集所有文件名,然后再对其进行处理,因此我尝试了以下方法:Python 递归函数的屈服,python,recursion,iterator,directory-structure,yield,Python,Recursion,Iterator,Directory Structure,Yield,我正在尝试对给定路径下的所有文件执行某些操作。我不想事先收集所有文件名,然后再对其进行处理,因此我尝试了以下方法: 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): expl
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
但这段代码在命中目录时跳过目录,而不是生成目录内容。我做错了什么?使用而不是重新发明轮子
特别是,以下是库文档中的示例,这是一个未经测试的尝试:
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
试试这个:
if stat.S_ISDIR(stat_info.st_mode):
for p in explore(path):
yield p
像函数一样调用
explore
。您应该做的是像生成器一样迭代它:
if stat.S_ISDIR(stat_info.st_mode):
for p in explore(path):
yield p
else:
yield path
编辑:您可以使用
os.path.isdir(path)
迭代器不会像那样递归工作,而不是stat
模块。您必须通过替换来重新生成每个结果
explore(路径)
有点像
explore(路径)中的值的:
收益率
按照中的建议,添加了X的语法yield from X
,以达到此目的。使用它,您可以执行以下操作:
explore的收益(路径)
如果使用,此语法还支持使用将值传递回递归调用的生成器。上面的循环的简单。将不会改变这一点:
explore(path)
为此:
for subpath in explore(path):
yield subpath
或者使用phooji建议的os.walk
。问题在于这行代码:
explore(path)
它有什么作用
- 使用新的
路径调用explore
探索
运行,创建生成器
- 生成器返回到执行
explore(path)
的位置强>
- 并且被丢弃
为什么要丢弃它?它没有被分配给任何东西,也没有被迭代——它被完全忽略了
如果你想对结果做点什么,那么,你必须对结果做点什么!;)
修复代码的最简单方法是:
for name in explore(path):
yield name
当您确信自己了解了正在发生的事情时,您可能希望使用os.walk()
一旦迁移到Python 3.3(假设所有工作都按计划进行),您将能够使用新的yield from
语法,此时修复代码的最简单方法是:
yield from explore(path)
如果您需要遍历所有文件夹和子文件夹,那么os.walk非常好。如果你不需要的话,那就像用象枪杀苍蝇一样
但是,对于这种特定情况,os.walk可能是一种更好的方法。您也可以使用堆栈实现递归
然而,这样做并没有任何好处,除了这是可能的。如果您首先使用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('.'))
要回答最初的问题,关键是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
这应该是IMHO接受的答案,因为问题是关于产量和递归,而不是关于实现os.walk的最佳方式;-)!!!我在这个非常简单的循环中打破了我的头。。。事实上,所有其他答案都在同一条线上…谢谢你!提到3.3和迭代器是非常有用的。正如在PyCon2014的演讲中提到的,生成器可以用来绕过递归限制!给出工作代码是好的,但解释OP做错了什么,特别是当他们要求这样做时,就更好了。问题是关于产量和递归,而不是实现os的最佳方式。walk也:在Python 2中,walk比listdir慢,请看一些语言可以产生整个序列,而不仅仅是单个元素。我不认为Python是其中之一。由于标题提出了一个比OS.WORK解决的更普遍的问题,请考虑如下:DEF探索(p):如果是实例(p,(list,tuple)):对于p中的x:探索(p),否则:屈服p,这有同样的问题。为什么不起作用?