递归函数的产量
我正在尝试对给定路径下的所有文件执行某些操作。我不想事先收集所有文件名然后对它们执行某些操作,所以我尝试了以下方法:
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
但是此代码在遇到目录时会跳过目录,而不是生成它们的内容。我做错了什么?
I am trying to do something to all the files under a given path. I don't want to collect all the file names beforehand then do something with them, so I tried this:
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
But this code skips over directories when it hits them, instead of yielding their contents. What am I doing wrong?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(9)
迭代器不会像这样递归地工作。您必须重新生成每个结果,方法是替换
为
Python 3.3 添加了语法
yield正如 PEP 380 中所提议的,来自 X
来实现此目的。有了它,你可以这样做:如果你使用 生成器作为协程,此语法还支持使用
generator.send()
传递值回到递归调用的生成器。上面的简单for
循环则不会。Iterators do not work recursively like that. You have to re-yield each result, by replacing
with something like
Python 3.3 added the syntax
yield from X
, as proposed in PEP 380, to serve this purpose. With it you can do this instead:If you're using generators as coroutines, this syntax also supports the use of
generator.send()
to pass values back into the recursively-invoked generators. The simplefor
loop above would not.问题是这行代码:
它做了什么?
path
调用explore
explore
运行,创建一个生成器,explore(path)
的位置代码> 已执行。 。 .为什么会被丢弃?它没有被分配给任何东西,也没有被迭代——它被完全忽略了。
如果你想对结果做点什么,那么你就必须对结果做点什么! ;)
修复代码的最简单方法是:
当您确信自己了解正在发生的情况时,您可能需要使用 os.walk() 来代替。
迁移到 Python 3.3 后(假设一切按计划进行),您将能够使用新的
yield from
语法,此时修复代码的最简单方法是:The problem is this line of code:
What does it do?
explore
with the newpath
explore
runs, creating a generatorexplore(path)
was executed . . .Why is it discarded? It wasn't assigned to anything, it wasn't iterated over -- it was completely ignored.
If you want to do something with the results, well, you have to do something with them! ;)
The easiest way to fix your code is:
When you are confident you understand what's going on, you'll probably want to use
os.walk()
instead.Once you have migrated to Python 3.3 (assuming all works out as planned) you will be able to use the new
yield from
syntax and the easiest way to fix your code at that point will be:使用
os.walk
而不是重新发明车轮。特别是,按照库文档中的示例,以下是未经测试的尝试:
Use
os.walk
instead of reinventing the wheel.In particular, following the examples in the library documentation, here is an untested attempt:
将其更改
为:
或使用 os.walk,如 phooji 建议的那样(这是更好的选择)。
Change this:
To this:
Or use
os.walk
, as phooji suggested (which is the better option).这就像函数一样调用
explore
。您应该做的是像生成器一样迭代它:编辑:您可以使用 os.path.isdir(path) 代替 stat 模块。
That calls
explore
like a function. What you should do is iterate it like a generator:EDIT: Instead of the
stat
module, you could useos.path.isdir(path)
.试试这个:
Try this:
如果您需要遍历所有文件夹和子文件夹,那么 os.walk 非常有用。如果你不需要这个,就像用大象枪打死苍蝇一样。
然而,对于这种特定情况,os.walk 可能是更好的方法。
os.walk is great if you need to traverse all the folders and subfolders. If you don't need that, it's like using an elephant gun to kill a fly.
However, for this specific case, os.walk could be a better approach.
您还可以使用堆栈来实现递归。
但这样做实际上并没有任何好处,除了它是可能的这一事实。如果您一开始就使用 python,那么性能提升可能不值得。
You can also implement the recursion using a stack.
There is not really any advantage in doing this though, other than the fact that it is possible. If you are using python in the first place, the performance gains are probably not worthwhile.
要回答最初提出的问题,关键是
yield
语句需要从递归中传播回来(就像return
一样)。这是 os.walk() 的工作重新实现。我在伪 VFS 实现中使用它,其中我还替换了 os.listdir() 和类似的调用。To answer the original question as asked, the key is that the
yield
statement needs to be propagated back out of the recursion (just like, say,return
). Here is a working reimplementation ofos.walk()
. I'm using this in a pseudo-VFS implementation, where I additionally replaceos.listdir()
and similar calls.