【问题标题】:Why is os.scandir() as slow as os.listdir()?为什么 os.scandir() 和 os.listdir() 一样慢?
【发布时间】:2020-04-03 17:25:57
【问题描述】:

我尝试通过使用 os.scandir() 而不是 os.listdir() 在 Windows 上优化用 Python 编写的文件浏览功能。但是,时间保持不变,大约 2 分半钟,我不知道为什么。 以下是功能,原始和更改:

os.listdir() 版本:

def browse(self, path, tree):
    # for each entry in the path
    for entry in os.listdir(path):
        entity_path = os.path.join(path, entry)
        # check if support by git or not
        if self.git_ignore(entity_path) is False:
            # if is a dir create a new level in the tree
            if os.path.isdir( entity_path ):
                tree[entry] = Folder(entry)
                self.browse(entity_path, tree[entry])
            # if is a file add it to the tree
            if os.path.isfile(entity_path):
                tree[entry] = File(entity_path)

os.scandir() 版本:

def browse(self, path, tree):
    # for each entry in the path
    for dirEntry in os.scandir(path):
        entry_path = dirEntry.name
        entity_path = dirEntry.path
        # check if support by git or not
        if self.git_ignore(entity_path) is False:
            # if is a dir create a new level in the tree
            if dirEntry.is_dir(follow_symlinks=True):
                tree[entry_path] = Folder(entity_path)
                self.browse(entity_path, tree[entry_path])
            # if is a file add it to the tree
            if dirEntry.is_file(follow_symlinks=True):
                tree[entry_path] = File(entity_path)

另外,这里使用的辅助函数如下:

def git_ignore(self, filepath):
    if '.git' in filepath:
        return True
    if '.ci' in filepath:
        return True
    if '.delivery' in filepath:
        return True
    child = subprocess.Popen(['git', 'check-ignore', str(filepath)],
                         stdout=subprocess.PIPE,
                         stderr=subprocess.PIPE)
    output = child.communicate()[0]
    status = child.wait()
    return status == 0

============================================================

class Folder(dict):
    def __init__(self, path):
        self.path = path
        self.categories = {}

============================================================

class File(object):
    def __init__(self, path):
        self.path = path
        self.filename, self.extension = os.path.splitext(self.path)

有没有人可以解决如何使函数运行得更快?我的假设是在开始时提取名称和路径会使其运行速度比应有的慢,对吗?

【问题讨论】:

  • 对于每个不包含“.git”、“.ci”或“.delivery”的路径,您正在生成一个 git 子进程。这很昂贵,如果你有很多这样的路径,那么生成和等待 git 进程所花费的累积时间将是一个瓶颈。

标签: python windows filesystems scandir listdir


【解决方案1】:

关于您的问题:

os.walk 似乎调用 stats 的次数超过了必要的次数。这似乎是它比 os.scandir() 慢的原因。

在这种情况下,我认为提高速度表现的最佳方法是 使用并行处理,这可以在某些循环中极大地提高速度。 关于这个问题有多个帖子。这里一个: Parallel Processing in Python – A Practical Guide with Examples.


不过,我还是想分享一些关于它的想法。

我也一直想知道这三个选项(scandir、listdir、walk)的最佳用法是什么。关于性能比较的文档不多。可能最好的方法是像你一样自己测试它。这是我的结论:

os.listdir()的用法

与 os.scandir() 相比,它似乎没有优势,只是更容易理解。当我只需要列出目录中的文件时,我仍然使用它。

优点:

  • 快速简单

缺点:

  • 太简单了,仅适用于列出目录中的文件和目录,因此您可能需要将其与其他方法结合使用以获取有关文件元数据的额外功能。如果是这样,最好使用 os.scandir()。

os.walk()的用法

当我们需要获取目录(和子目录)中的所有项目时,这是最常用的功能。

优点:

  • 这可能是遍历所有项目路径和名称的最简单方法。

缺点:

  • 似乎调用统计信息的次数超出了必要的次数。这似乎是它比 os.scandir() 慢的原因。
  • 虽然它为您提供了文件的根部分,但它不提供 os.scandir() 的额外元信息。

os.scandir()的用法

它似乎(几乎)两全其美。它为您提供简单的 os.listdir 的速度,并具有额外的功能,可让您 简化循环,因为您可以避免使用 exiftool 或其他元数据工具 当您需要有关文件的额外信息时。

优点:

  • 快。与 os.listdir() 速度相同
  • 非常不错的额外功能。

缺点:

  • 如果您想深入了解子文件,您需要创建另一个函数来扫描每个子目录。这个函数非常简单,但在这种情况下使用 os.walk 可能更符合 Python 风格(我的意思是更优雅的 sintax)。

这就是我在阅读并使用它们之后的看法。很高兴得到更正,因此我可以了解更多信息。

【讨论】:

  • 一句“谢谢!”以获得全面而有条理的答案。我的理解更清晰了。
猜你喜欢
  • 1970-01-01
  • 2011-05-21
  • 2021-03-26
  • 2017-07-25
  • 2018-11-04
  • 2012-04-03
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多