为什么 os.scandir() 和 os.listdir() 一样慢?

Vla*_*che 5 python windows filesystems scandir listdir

我尝试在 Windows 上使用 os.scandir() 而不是 os.listdir() 来优化用 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)
Run Code Online (Sandbox Code Playgroud)

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)
Run Code Online (Sandbox Code Playgroud)

此外,这里是其中使用的辅助功能:

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)
Run Code Online (Sandbox Code Playgroud)

有没有人可以解决如何使函数运行得更快的解决方案?我的假设是在开始时提取名称和路径使它运行得比它应该的慢,这是正确的吗?

Jav*_*avi 5

关于你的问题:

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

在这种情况下,我认为提高速度性能的最佳方法是使用并行处理,这可以在某些循环中令人难以置信地提高速度。关于这个问题有很多帖子。这里有一个: Python 中的并行处理——带有示例的实用指南


尽管如此,我还是想分享一些关于它的想法。

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

os.listdir() 的用法

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

优点:

  • 快速简单

缺点:

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

os.walk() 的用法

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

优点:

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

缺点:

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

os.scandir() 的用法

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

优点:

  • 快速地。与 os.listdir() 相同的速度
  • 非常好的附加功能。

缺点:

  • 如果你想深入研究子文件,你需要创建另一个函数来扫描每个子目录。这个函数非常简单,但在这种情况下使用 os.walk 可能会更 Pythonic(我只是指更优雅的语法)。

这就是我阅读并使用它们后的看法。我很高兴得到纠正,所以我可以了解更多。

  • 我赞成这个精彩的答案,但如果有人通过真实的速度测量来支持这一点,那就太好了。 (3认同)
  • 只是简短地说一声“谢谢!” 以获得全面且组织良好的答案。我的理解就清楚多了。 (2认同)