Python文件系统遍历性能优化核心是减少系统调用:优先用os.scandir()替代os.listdir(),利用DirEntry复用内核缓存信息;Python 3.12+推荐Path.walk();按需调用entry.stat(),避免重复解析路径和无谓stat();善用glob.iglob等惰性接口。

Python中遍历文件系统时,性能瓶颈往往不在代码逻辑本身,而在于I/O调用频次、路径解析开销和不必要的元数据读取。优化核心是减少系统调用次数、避免重复解析、按需获取属性,并合理利用现代API。
优先使用 os.scandir() 替代 os.listdir()
os.scandir() 在遍历目录时直接返回 DirEntry 对象,其 stat() 和 is_dir() 等方法可复用内核已读取的目录项信息,避免额外的 stat() 系统调用。而 os.listdir() + 单独 os.path.isdir() 会为每个条目触发两次系统调用(一次读目录,一次查属性)。
- ✅ 推荐写法:
for entry in os.scandir(path): if entry.is_dir(): ... - ❌ 低效写法:
for name in os.listdir(path): if os.path.isdir(os.path.join(path, name)): ...
用 pathlib.Path.walk()(Python 3.12+)或 os.walk() 的高效变体
Python 3.12 引入了 Path.walk(),底层基于 os.scandir(),支持提前终止、跳过子目录,且接口更简洁。若使用旧版本,可手动封装 os.scandir() 实现类 walk 行为,跳过 os.path.join() 频繁拼接:
- 对每个
DirEntry,用entry.path(Python 3.12+)或os.path.join(dir_path, entry.name)获取完整路径 - 避免在循环内反复调用
os.path.abspath()或正则匹配全路径 - 如只需过滤特定后缀,用
entry.name.endswith(('.py', '.txt'))比Path(entry.path).suffix in {'.py', '.txt'}更快
按需获取文件属性,避免无谓 stat()
很多场景只需判断是否为目录/文件,或检查修改时间,不必调用 os.stat() 获取全部字段。DirEntry 的 is_file()、is_dir()、stat(follow_symlinks=False) 均可复用缓存信息;若需大小或时间,再显式调用 entry.stat().st_size 或 .st_mtime。
立即学习“Python免费学习笔记(深入)”;
- 不要写
st = os.stat(p); if st.st_size > 0 and stat.S_ISREG(st.st_mode): ... - 改用
if entry.is_file() and entry.stat().st_size > 0:—— 只在确认是文件后才读大小
批量处理与 I/O 合并策略
当需对大量小文件做轻量操作(如收集路径、检查存在性),可考虑分批处理以缓解GIL影响;对需要内容读取的场景,避免逐个 open().read(),可借助 concurrent.futures.ThreadPoolExecutor 并发读取(注意磁盘随机IO仍是瓶颈)。另外,若目标是统计或搜索,可结合 glob(配合 ** 和 recursive=True)快速定位,它内部也已优化为基于 scandir。
- 简单通配推荐:
list(Path(".").glob("**/*.py"))(内存友好,惰性生成器可用glob.iglob) - 避免递归前先
os.listdir()再手动拼接——这破坏了scandir的缓存优势











