递归函数中的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):
explore(path)
else:
yield path
if __name__ == "__main__":
for x in explore('.'):
print '-->', x
但是这个代码在遇到文件夹时会跳过它们,而不是处理它们里面的内容。我哪里做错了呢?
9 个回答
37
问题出在这行代码上:
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)
129
迭代器并不是像那样递归工作的。你需要重新返回每一个结果,也就是说要把
explore(path)
换成类似这样的东西
for value in explore(path):
yield value
Python 3.3 增加了一种语法 yield from X
,这是根据PEP 380 提出的,目的是为了简化这个过程。这样你就可以这样做:
yield from explore(path)
如果你在使用生成器作为协程,这个语法还支持使用generator.send()
将值传回递归调用的生成器。上面简单的 for
循环是无法做到的。
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