跳转到主要内容

scandir,更好的目录迭代器和更快的os.walk()

项目描述

scandir on PyPI (Python Package Index) Travis CI tests (Linux) Appveyor tests (Windows)

scandir() 是一个目录迭代函数,类似于 os.listdir(),但它返回的不是裸文件名列表,而是包含文件类型和状态信息以及名称的 DirEntry 对象。使用 scandir() 可以通过避免大多数情况下不必要的 os.stat() 调用,将 os.walk() 的速度提高 2-20 倍(取决于平台和文件系统)。

现在已包含在您附近的Python中!

scandir 已经被包含在 Python 3.5 标准库中,作为 os.scandir(),并且对 os.walk() 的相关性能改进也已被包含。所以如果你足够幸运,正在使用 Python 3.5(发布日期 2015 年 9 月 13 日),你将立即获得这些好处,否则只需从 PyPI 下载此模块,使用 pip install scandir 安装,然后在你的代码中执行类似以下操作

# Use the built-in version of scandir/walk if possible, otherwise
# use the scandir module version
try:
    from os import scandir, walk
except ImportError:
    from scandir import scandir, walk

PEP 471,这是一个提议将 scandir 包含到 Python 标准库中的 PEP,由 PEP 的 BDFL-delegate Victor Stinner 在 2014 年 7 月接受

scandir 模块旨在与 Python 2.7+ 和 Python 3.4+ 一起工作(并且已经在这些版本上进行了测试)。

背景

Python 内置的 os.walk() 比它需要的要慢得多,因为它除了在每一个目录上调用 listdir() 之外,还要对每一个文件调用 stat() 来确定文件名是否是目录。但是 Windows 上的 FindFirstFile / FindNextFile 和 Linux/OS X 上的 readdir 已经告诉你是目录还是文件,所以不需要进一步的 stat 系统调用。简而言之,你可以将系统调用的次数从大约 2N 减少到 N,其中 N 是树中文件和目录的总数。

实际上,移除所有这些额外的系统调用使得 os.walk() 在 Windows 上快了约 7-50 倍,在 Linux 和 Mac OS X 上快了约 3-10 倍。所以我们讨论的不是微优化。下面是更多基准测试的结果。

与之相关的是,许多人也要求一个在迭代时产生文件名而不是作为一个大列表返回的 os.listdir() 版本。这提高了迭代非常大的目录时的内存效率。

因此,除了更快的 walk() 之外,scandir 还添加了一个新的 scandir() 函数。它们非常容易使用,但请参阅下面的“API”以获取完整文档。

基准测试

以下是使用不带参数运行 benchmark.py 得到的结果,显示了在不同系统上 scandir.walk() 比较之 os.walk() 快多少倍

系统版本

Python 版本

倍数

Windows 7 64-bit

2.7.7 64-bit

10.4

Windows 7 64-bit SSD

2.7.7 64-bit

10.3

Windows 7 64-bit NFS

2.7.6 64-bit

36.8

Windows 7 64-bit SSD

3.4.1 64-bit

9.9

Windows 7 64-bit SSD

3.5.0 64-bit

9.5

Ubuntu 14.04 64-bit

2.7.6 64-bit

5.8

Mac OS X 10.9.3

2.7.5 64-bit

3.8

上述所有测试都是使用 scandir 的快速 C 版本进行的(源代码在 _scandir.c)。

请注意,在较小的目录上收益小于上述,而在较大的目录上收益更大。这就是为什么 benchmark.py 创建了一个具有标准化大小的测试目录树的原因。

API

walk()

scandir.walk() 的 API 与 os.walk() 完全相同,所以只需阅读 Python 文档

scandir()

scandir() 和它产生的 DirEntry 对象的完整文档可以在此处找到。但以下是一个简要的总结。

scandir(path=’.’) -> 给定路径的 DirEntry 对象的迭代器

listdir 类似,scandir 调用操作系统的目录迭代系统调用以获取给定 path 中的文件名,但它与 listdir 有两点不同

  • 它返回轻量级的 DirEntry 对象,而不是裸露的文件名字符串,这些对象包含文件名字符串并提供简单的方法,允许访问操作系统可能返回的额外数据。

  • 它返回一个生成器而不是列表,因此 scandir 作为真正的迭代器而不是立即返回完整列表。

scandir()path 中的每个文件和子目录生成一个 DirEntry 对象。就像 listdir 一样,跳过了 '.''..' 伪目录,并且条目按系统依赖的顺序生成。每个 DirEntry 对象都有以下属性和方法

  • name:条目的文件名,相对于 scandir path 参数(对应于 os.listdir 的返回值)

  • path:条目的完整路径名(不一定是绝对路径)- 等同于 os.path.join(scandir_path, entry.name)

  • is_dir(*, follow_symlinks=True):类似于 pathlib.Path.is_dir(),但返回值在 DirEntry 对象上缓存;大多数情况下不需要系统调用;如果 follow_symlinks 为 False,则不跟随符号链接

  • is_file(*, follow_symlinks=True):类似于 pathlib.Path.is_file(),但返回值在 DirEntry 对象上缓存;大多数情况下不需要系统调用;如果 follow_symlinks 为 False,则不跟随符号链接

  • is_symlink():类似于 pathlib.Path.is_symlink(),但返回值在 DirEntry 对象上缓存;大多数情况下不需要系统调用

  • stat(*, follow_symlinks=True):类似于 os.stat(),但返回值在 DirEntry 对象上缓存;在 Windows 上不需要系统调用(除了符号链接);如果 follow_symlinks 为 False,则不跟随符号链接(如 os.lstat()

  • inode():返回条目的inode号;返回值在 DirEntry 对象上缓存

以下是一个非常简单的 scandir() 示例,展示了 DirEntry.name 属性和 DirEntry.is_dir() 方法的使用

def subdirs(path):
    """Yield directory names not starting with '.' under given path."""
    for entry in os.scandir(path):
        if not entry.name.startswith('.') and entry.is_dir():
            yield entry.name

subdirs() 函数在使用 scandir 时比在 Windows 和 POSIX 系统上使用 os.listdir()os.path.isdir() 快得多,特别是在中等大小或大型目录中。

进一步阅读

  • Python scandir 文档

  • PEP 471,这是一项(现已接受)的 Python 增强提案,建议将 scandir 添加到标准库中 - 这里有很多细节,包括被拒绝的想法和以前的讨论

火焰、评论、错误报告

请将关于 scandir 的火焰、评论和问题发送给 Ben Hoyt

http://benhoyt.com/

在此处报告 Python 3.5 标准库中版本的错误报告 here,或在此模块的 GitHub 项目页面上提交错误报告或功能请求

https://github.com/benhoyt/scandir

项目详情


下载文件

下载适合您平台的文件。如果您不确定选择哪个,请了解有关安装包的更多信息。

源分发

scandir-1.10.0.tar.gz (33.3 kB 查看哈希值)

上传时间

构建分发

scandir-1.10.0-cp37-cp37m-win_amd64.whl (22.8 kB 查看哈希值)

上传时间 CPython 3.7m Windows x86-64

scandir-1.10.0-cp37-cp37m-win32.whl (22.1 kB 查看哈希值)

上传时间 CPython 3.7m Windows x86

scandir-1.10.0-cp36-cp36m-win_amd64.whl (22.8 kB 查看哈希值)

上传时间 CPython 3.6m Windows x86-64

scandir-1.10.0-cp36-cp36m-win32.whl (22.1 kB 查看哈希值)

上传时间 CPython 3.6m Windows x86

scandir-1.10.0-cp35-cp35m-win_amd64.whl (22.8 kB 查看哈希值)

上传时间 CPython 3.5m Windows x86-64

scandir-1.10.0-cp35-cp35m-win32.whl (22.1 kB 查看哈希值)

上传时间 CPython 3.5m Windows x86

scandir-1.10.0-cp34-cp34m-win_amd64.whl (20.5 kB 查看哈希值)

上传时间 CPython 3.4m Windows x86-64

scandir-1.10.0-cp34-cp34m-win32.whl (20.2 kB 查看哈希值)

上传时间 CPython 3.4m Windows x86

scandir-1.10.0-cp27-cp27m-win_amd64.whl (20.9 kB 查看哈希值)

上传时间: CPython 2.7m Windows x86-64

scandir-1.10.0-cp27-cp27m-win32.whl (20.5 kB 查看哈希值)

上传时间: CPython 2.7m Windows x86

支持者

AWS AWS 云计算和安全赞助商 Datadog Datadog 监控 Fastly Fastly CDN Google Google 下载分析 Microsoft Microsoft PSF 赞助商 Pingdom Pingdom 监控 Sentry Sentry 错误日志 StatusPage StatusPage 状态页面