【问题标题】:Travel directory tree with limited recursion depth递归深度有限的旅行目录树
【发布时间】:2016-05-20 20:16:00
【问题描述】:

我需要递归处理目录树中的所有文件,但深度有限。

这意味着例如在当前目录和前两个子目录级别中查找文件,但不再进一步。在这种情况下,我必须处理例如./subdir1/subdir2/file,但不是./subdir1/subdir2/subdir3/file

如何在 Python 3 中做到最好?

目前我使用os.walk 在这样的循环中处理无限深度的所有文件:

for root, dirnames, filenames in os.walk(args.directory):
    for filename in filenames:
        path = os.path.join(root, filename)
        # do something with that file...

我可以想办法计算 root 中的目录分隔符 (/) 以确定当前文件的层次级别,如果该级别超过所需的最大值,则 break 循环。

当有大量子目录需要忽略时,我认为这种方法可能不安全并且效率可能非常低。这里的最佳方法是什么?

【问题讨论】:

标签: python python-3.x directory-structure subdirectory os.walk


【解决方案1】:

我认为最简单和最稳定的方法是复制os.walk straight out of the source 的功能并插入您自己的深度控制参数。

import os
import os.path as path

def walk(top, topdown=True, onerror=None, followlinks=False, maxdepth=None):
    islink, join, isdir = path.islink, path.join, path.isdir

    try:
        names = os.listdir(top)
    except OSError, err:
        if onerror is not None:
            onerror(err)
        return

    dirs, nondirs = [], []
    for name in names:
        if isdir(join(top, name)):
            dirs.append(name)
        else:
            nondirs.append(name)

    if topdown:
        yield top, dirs, nondirs

    if maxdepth is None or maxdepth > 1:
        for name in dirs:
            new_path = join(top, name)
            if followlinks or not islink(new_path):
                for x in walk(new_path, topdown, onerror, followlinks, None if maxdepth is None else maxdepth-1):
                    yield x
    if not topdown:
        yield top, dirs, nondirs

for root, dirnames, filenames in walk(args.directory, maxdepth=2):
    #...

如果您对所有这些可选参数不感兴趣,您可以大幅削减函数:

import os

def walk(top, maxdepth):
    dirs, nondirs = [], []
    for name in os.listdir(top):
        (dirs if os.path.isdir(os.path.join(top, name)) else nondirs).append(name)
    yield top, dirs, nondirs
    if maxdepth > 1:
        for name in dirs:
            for x in walk(os.path.join(top, name), maxdepth-1):
                yield x

for x in walk(".", 2):
    print(x)

【讨论】:

  • 对于一个小问题,这是一段相当长的代码......如果可能的话,我更喜欢更紧凑的解决方案。而且我认为您的意思是倒数第二行中的for ... in walk(...): 而不是os.walk,不是吗?
  • 有趣的是,我只是在编写一个较短的版本 :-) 倒数第二行的错误 os. 是对的;固定。
  • 那个短版看起来很酷。我将其修改为不返回目录(因为我只需要文件),并比较 if maxdepth != 0 以便 0 仅表示当前目录,我可以使用负值来遍历整个目录结构。
【解决方案2】:

从 python 3.5 开始,os.scandir 在 os.walk 中使用,而不是 os.listdir。它的工作速度要快很多倍。我稍微纠正了@kevin 示例。

import os

def walk(top, maxdepth):
    dirs, nondirs = [], []
    for entry in os.scandir(top):
        (dirs if entry.is_dir() else nondirs).append(entry.path)
    yield top, dirs, nondirs
    if maxdepth > 1:
        for path in dirs:
            for x in walk(path, maxdepth-1):
                yield x

for x in walk(".", 2):
    print(x)

【讨论】:

  • 在 Windows 上要快得多。并且有用于 python 的反向端口(@98​​7654322@ 模块)
  • walkMaxDepth 未定义。应该走路吗?
  • 有趣的是,两年来没有人关注这个错误。我从两个不同的地方获取代码,复制粘贴导致不同的名称。这是递归,而不是 walkMaxDepth 应该是主板函数 walk 的名称。我已经在代码中解决了这个问题。感谢您对此的关注。当完成的 sn-p 不起作用时,我自己也很痛苦。
  • 为了与 os.walk 足够相似,nodirs 列表应该仅包含基本名称。在这些解决方案中,它包含完整路径。这有点难看,但它会使 walk 和 os.walk 产生类似的结果:(dirs if entry.is_dir() else nondirs).append(entry.path if entry.is_dir() else os.path.basename(entry.path))
猜你喜欢
  • 2011-04-21
  • 1970-01-01
  • 1970-01-01
  • 2015-04-08
  • 2012-10-26
  • 2020-08-06
  • 2016-03-02
  • 1970-01-01
  • 2021-07-05
相关资源
最近更新 更多