为什么python的os.walk()不反映目录删除?

sea*_*ern 7 python os.walk

我正在尝试编写一个Python函数,它将递归删除所有空目录.这意味着如果目录"a"仅包含"b",则应删除"b",则应删除"a"(因为它现在不包含任何内容).如果目录包含任何内容,则会跳过该目录.图说:

top/a/b/
top/c/d.txt
top/c/foo/
Run Code Online (Sandbox Code Playgroud)

鉴于此,应删除三个目录"b","a"和"foo",因为"foo"和"b"现在为空,并且"a"在删除"b"后将变为空.

我试图通过os.walkshutil.rmtree.不幸的是,我的代码只删除了第一级目录,但没有删除过程中新清空的目录.

我正在使用topdown=false参数os.walk.该文档os.walk说:"如果自上而下为False,三联供的目录是三元的所有子目录后生成(目录生成由下而上)." 那不是我所看到的.

这是我的代码:

for root, dirs, files in os.walk(".", topdown=False):
  contents = dirs+files
  print root,"contains:",contents
  if len(contents) == 0:
    print 'Removing "%s"'%root
    shutil.rmtree(root)
  else:
    print 'Not removing "%s". It has:'%root,contents
Run Code Online (Sandbox Code Playgroud)

如果我有上面描述的目录结构,这是我得到的:

./c/foo contains: []
Removing "./c/foo"
./c contains: ['foo', 'd.txt']
Not removing "./c". It has: ['foo', 'd.txt']
./a/b contains: []
Removing "./a/b"
./a contains: ['b']
Not removing "./a". It has: ['b']
. contains: ['c', 'a']
Not removing ".". It has: ['c', 'a']
Run Code Online (Sandbox Code Playgroud)

请注意,即使我删除了"b",也没有删除"a",认为它仍然包含"b".令我困惑的是,文档os.walk说它在为"b"生成三元组生成"./a" 的三元组.我的输出表明不然.类似的故事为"./c".它表明它仍然有"foo",即使我已经将它删除了.

我究竟做错了什么?(我使用的是Python 2.6.6.)

jcf*_*wer 9

文件有这个...

无论topdown的值如何,都会在生成目录及其子目录的元组之前检索子目录列表.


Blc*_*ght 2

jcfollower 的答案对于您遇到的问题的原因是绝对正确的:文件系统始终是自上而下读取的,即使结果是以os.walk自下而上的方式产生的。这意味着您执行的文件系统修改不会反映在以后的结果中。

此问题的解决方案是维护一组已删除的目录,以便您可以将它们从其父目录的子目录列表中过滤掉:

removed = set()                                               # first new line
for root, dirs, files in os.walk(".", topdown=False):
      dirs = [dir for dir in dirs if os.path.join(root, dir) not in removed] # second
      contents = dirs+files
      print root,"contains:",contents
      if len(contents) == 0:
          print 'Removing "%s"'%root
          shutil.rmtree(root)
          removed.add(root)                                   # third new line
      else:
          print 'Not removing "%s". It has:'%root,contents
Run Code Online (Sandbox Code Playgroud)

有3条新线路。第一个在顶部创建一个空removed集来包含已删除的目录。第二个将dirs列表替换为新列表,该列表不包含已删除集中的任何子目录,因为它们已在上一步中删除。最后一个新行将当前目录添加到已删除的集合中。