【问题标题】:Python 2.7 - Using scandir to traverse all sub-directories and return listPython 2.7 - 使用scandir遍历所有子目录并返回列表
【发布时间】:2018-04-05 05:07:40
【问题描述】:

使用 Python 2.7 和 scandir,我需要遍历所有目录和子目录并仅返回目录列表。不是文件。路径中子目录的深度可能会有所不同。

我知道 os.walk,但我的目录有 200 万个文件,因此 os.walk 会因此变慢。

目前下面的代码适用于我,但我怀疑可能有更简单的方法/循环来实现相同的结果,我想知道如何改进它。另外我的功能的限制是它仍然受到我可以遍历到子目录的深度的限制,也许这可以克服。

def list_directories(path):
dir_list = []
for entry in scandir(path):
    if entry.is_dir():
        dir_list.append(entry.path)
        for entry2 in scandir(entry.path):
            if entry2.is_dir():
                dir_list.append(entry2.path)
                for entry3 in scandir(entry2.path):
                    if entry3.is_dir():
                        dir_list.append(entry3.path)
                        for entry4 in scandir(entry3.path):
                            if entry4.is_dir():
                                dir_list.append(entry4.path)
                                for entry5 in scandir(entry4.path):
                                    if entry5.is_dir():
                                        dir_list.append(entry5.path)
                                        for entry6 in scandir(entry5.path):
                                            if entry6.is_dir():
                                                dir_list.append(entry6.path)
return dir_list
for item in filelist_dir(directory):
    print item

如果您有更好的选择来快速返回包含数百万个文件的路径中的所有目录和子目录,请告诉我。

【问题讨论】:

标签: python directory-structure subdirectory scandir


【解决方案1】:

scandir 支持 walk() 函数,该函数包含与 scandir() 相同的优化,因此它应该比 os.walk() 更快。 (scandir 的 background section 建议在 Linux/Mac OS X 上提高 3-10 倍。)

所以你可以使用它......像这样的代码可能会起作用:

from scandir import walk

def list_directories(path):
    dir_list = []
    for root, _, _ in walk(path):
        # Skip the top-level directory, same as in your original code:
        if root == path:
            continue
        dir_list.append(root)
    return dir_list

如果您想使用 scandir() 来实现这一点,为了实现支持任意深度的东西,您应该使用递归。

类似:

from scandir import scandir

def list_directories(path):
    dir_list = []
    for entry in scandir(path):
        if entry.is_dir() and not entry.is_symlink():
            dir_list.append(entry.path)
            dir_list.extend(list_directories(entry.path))
    return dir_list

注意:我也添加了对 is_symlink() 的检查,因此它不会遍历符号链接。否则一个指向'.'的符号链接。或 '..' 会使这个递归永远......

我仍然认为使用 scandir.walk() 更好(更简单、更可靠),所以如果适合您,请改用它!

【讨论】:

  • 谢谢@Filipe,我可以请您编辑您的答案以包含使用 scandir.walk() 执行此操作的示例吗?再次感谢...
  • 聪明的@Filipe,你的回答对我有帮助。
  • 我收到 No module named 'scandir' 错误。这可能是因为我使用的是 Python 3.x 吗?
  • @tzg scandir 不是来自标准库,它来自 PyPI,所以你应该使用 pip 或类似的方式安装它。 (这个问题明确询问了scandir,这就是为什么在答案中没有提到这一点。)
  • 我和原始发帖人有同样的问题,因为我有数百万个文件的目录并且 os.walk 很慢。
【解决方案2】:

首先,为了避免 6 个目录的限制,您可能希望递归地执行此操作:

def list_directories(path):
    dir_list = []
    for entry in scandir(path):
        if entry.is_dir():
            dir_list.append(entry.path)
            dir_list.extend(list_directories(entry.path))

另外,由于您使用的是 Python 2.7,os.walk 太慢的部分原因是 Python 2.7 使用 listdir 而不是 scandir 代替 walkscandir backport package 包括它自己的 walk 实现(与 Python 3.5 中使用的基本相同),它提供与 walk 相同的 API,但具有很大的加速(尤其是在 Windows 上)。


除此之外,您的主要性能成本可能取决于平台。

在 Windows 上,主要是读取目录条目的成本。你真的无能为力。 scandir 已经是最快的方式了。

在 POSIX 上,可能主要是 stat 每个文件查看它是否是目录的成本。您可以通过使用fts(尤其是在 Linux 上)来加快速度,但据我所知,没有好的 Python 包装器。如果您知道ctypes,那么调用它并不复杂;困难的部分是想出一个好的设计来将它的所有特性暴露给 Python(当然,你不需要这样做)。如果您想亲自尝试,请参阅my unfinished library on GitHub


或者,您可能想要使用find(它在幕后使用fts),或者通过subprocess 驱动它,或者让它驱动您的脚本。


最后,您可能希望并行执行操作。如果您的文件系统是旧笔记本电脑硬盘驱动器,而不是两个 SSD 和带有高端控制器的 RAID 条带,这实际上可能会减慢速度而不是加快速度。所以在提交太多之前一定要试试看。

如果您正在做不平凡的工作,那么您可能只需要一个线程来排队等待您的工作人员工作的目录。

如果步行是重点,您需要并行拉动多个步行者。 concurrent.futures.ThreadPoolExecutor 的包装方式可能开箱即用,而且非常简单。为了获得最大速度,您可能需要手动排队并分批拉取它们,按物理卷对工作进行分片等,但可能这些都不是必需的。 (如果是这样,并且如果你能在阅读 Rust 代码时糊里糊涂,ripgrep 会尽可能快地浏览文件系统。)

【讨论】:

  • 谢谢@abarnert ...这个答案也是100%正确的。感谢您的帮助。
【解决方案3】:

你可以使用python内置模块os.walk

for root, dirs, files in os.walk(".", topdown=False):
   for name in files:
      print(os.path.join(root, name))
   for name in dirs:
      #this will get your all directories within the path
      print(os.path.join(root, name))

欲了解更多信息,请访问此链接:os.walk

【讨论】:

  • 问题是“我知道 os.walk,但我的目录有 200 万个文件,因此 os.walk 会因此变慢。”
猜你喜欢
  • 2018-11-15
  • 2020-08-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-11-04
相关资源
最近更新 更多