在递归函数中产生

Ali*_*Ali 52 python recursion iterator yield directory-structure

我正在尝试对给定路径下的所有文件执行某些操作.我不想事先收集所有的文件名然后用它们做一些事情,所以我尝试了这个:

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
Run Code Online (Sandbox Code Playgroud)

但是这个代码在命中它们时会跳过目录,而不是让它们产生内容.我究竟做错了什么?

小智 126

迭代器不会像那样递归地工作.您必须通过替换重新产生每个结果

explore(path)
Run Code Online (Sandbox Code Playgroud)

喜欢的东西

for value in explore(path):
    yield value
Run Code Online (Sandbox Code Playgroud)

Python 3.3添加了PEP 380中yield from X提出的语法来实现此目的.有了它,你可以这样做:

yield from explore(path)
Run Code Online (Sandbox Code Playgroud)

如果您使用生成器作为协同程序,则此语法还支持使用generator.send()将值传递回递归调用的生成器.for上面的简单循环不会.

  • 这应该是接受的答案恕我直言,因为问题是关于产量和递归而不是关于实现os.walk的最佳方式;-) !!! 我在这个非常简单的循环中突破了......实际上所有其他答案都在同一条线上...... (17认同)

Eth*_*man 36

问题是这行代码:

explore(path)
Run Code Online (Sandbox Code Playgroud)

它有什么作用?

  • explore用新电话打电话path
  • explore 运行,创建一个发电机
  • 生成器返回到explore(path)执行的位置...
  • 并被丢弃

它为什么被丢弃?它没有分配给任何东西,它没有被迭代 - 它被完全忽略了.

如果你想对结果做点什么,那么你必须对它们做点什么!;)

修复代码的最简单方法是:

for name in explore(path):
    yield name
Run Code Online (Sandbox Code Playgroud)

如果您确信自己了解正在发生的事情,那么您可能希望使用它os.walk().

一旦迁移到Python 3.3(假设所有工作都按计划完成),您将能够使用新yield from语法,最简单的方法是在此时修复代码:

yield from explore(path)
Run Code Online (Sandbox Code Playgroud)


pho*_*oji 25

使用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
Run Code Online (Sandbox Code Playgroud)

  • 提供工作代码是好的,但解释OP做错了什么,特别是当他们提出要求时,甚至更好. (15认同)
  • 问题是关于yield和递归,而不是关于实现os.walk的最佳方法 (3认同)

Die*_*Epp 8

改变这个:

explore(path)
Run Code Online (Sandbox Code Playgroud)

对此:

for subpath in explore(path):
    yield subpath
Run Code Online (Sandbox Code Playgroud)

或者os.walk像phooji建议的那样使用(这是更好的选择).