繁体   English   中英

Python 2.7-使用scandir遍历所有子目录并返回列表

[英]Python 2.7 - Using scandir to traverse all sub-directories and return list

使用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

请让我知道,如果您有更好的替代方法,可以快速返回包含数百万个文件的路径中的所有目录和子目录。

scandir支持walk()函数,该函数包含对scandir()的相同优化,因此它应该比os.walk()更快。 (scandir的背景部分建议在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()更好(更简单,更可靠),因此如果适合您,请改用它!

首先,为了避免限制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进行walk scandir backport软件包包括自己的walk实现(与Python 3.5中使用的基本相同),它提供与walk相同的API,但具有极大的提速(特别是在Windows上)。


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

在Windows上,主要是读取目录条目的成本。 实际上,您对此无能为力。 scandir已经以最快的方式执行此操作。

在POSIX上,可能主要是stat每个文件是否为目录的成本。 您可以使用fts加快速度(尤其是在Linux上),但是据我所知,没有适合它的Python包装器。 如果您知道ctypes ,则调用它并不那么复杂; 困难的部分是想出一个好的设计,以便将其所有功能公开给Python(当然,您不需要这样做)。 如果您想自己尝试,请在GitHub上查看我未完成的库


或者,您可能需要使用find (使用fts被窝里),或者通过驾驶它subprocess ,或有它推动你的脚本。


最后,您可能希望并行执行操作。 如果您的文件系统是旧笔记本电脑硬盘,而不是例如两个SSD和带有高端控制器的RAID条带,那么这实际上可能会减慢速度,而不是加快速度。 因此,绝对可以尝试一下,然后再投入太多。

如果您要做的事情不那么繁琐,那么可能只需要一个步行线程就可以为工作人员排队以便进行工作的目录。

如果步行是重点,那么您将希望并行拉动多个步行者。 concurrent.futures.ThreadPoolExecutor打包内容的方式可能刚好够好,而且非常简单。 为了获得最大速度,您可能需要手动排队并分批拉动它们,按物理量对工作进行分片等,但是可能不需要这样做。 (如果是这样,并且如果您可以混淆阅读Rust代码, ripgrep进行大量工作以尽可能快地浏览文件系统。)

您可以使用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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM