Path.walk 怎样控制目录遍历
来源:17golang原创
时间:2026-10-25 13:38:21 372浏览 收藏
如果要控制 Python 的目录遍历,核心不是在循环体里反复判断完整路径,而是正确使用 Path.walk() 返回的 dirnames 和它的三个参数:top_down 决定目录产出的方向,on_error 决定扫描失败怎么处理,follow_symlinks 决定符号链接是否参与深入遍历。官方地址:https://docs.python.org/3/library/pathlib.html。
最常用的控制方式是保持默认的top_down=True,在每次产出目录后原地修改dirnames;对权限问题使用on_error明确记录或抛出,对符号链接则默认不跟随,只有确认目录图不会形成环时才考虑打开。
- 观察口径:每次产出一个目录元组,分别统计目录数量和文件数量。
- 控制点:自顶向下时删改
dirnames,可以阻止后续进入某些子目录。 - 边界点:
on_error接收OSError,follow_symlinks=True可能导致无限递归。
先读懂 Path.walk 的三元组
Path.walk() 从 Python 3.12 起提供目录树遍历接口。每次迭代返回 (dirpath, dirnames, filenames):dirpath 是当前目录的 Path,另外两个是当前目录下的名称列表,而不是已经拼好的绝对路径。读取具体项目时,要用 dirpath / name 组合出完整路径。
列表顺序由文件系统决定,不能把返回顺序当作排序结果。一个简单的观察器可以把目录数、文件数和具体路径分开记录;这比只打印一串混合路径更容易确认过滤是否生效。
from pathlib import Path
def inspect_tree(root: Path) -> None:
directory_count = 0
file_count = 0
for dirpath, dirnames, filenames in root.walk():
# dirnames 和 filenames 只有名称,拼接时保留当前目录作为基准。
directory_count += len(dirnames)
file_count += len(filenames)
print("目录:", dirpath)
print("子目录:", [dirpath / name for name in dirnames])
print("文件:", [dirpath / name for name in filenames])
# 这些是示例统计口径,不代表某台机器上的性能结果。
print("目录项数量:", directory_count)
print("文件项数量:", file_count)
这里的两个计数只描述本次遍历看到的项目数量,不代表耗时、吞吐或内存性能。实际项目还可以把 filenames 交给后续后缀过滤,但不要直接假设列表已经排序。

用 top_down 和 dirnames 控制范围
默认的 top_down=True 会先产出当前目录,再处理子目录。因此,在当前元组交给循环体后,可以原地删除不想进入的目录名。这个修改必须作用于当前的 dirnames 列表;重新给变量绑定一个新列表,不一定能改变遍历器使用的目录集合。
from pathlib import Path
def iter_source_files(root: Path):
ignored = {".git", "__pycache__", "node_modules"}
for dirpath, dirnames, filenames in root.walk(top_down=True):
# 原地过滤,遍历器就不会继续深入这些目录。
dirnames[:] = [name for name in dirnames if name not in ignored]
for name in filenames:
# 只把当前目录下的 Python 文件交给调用方。
if name.endswith(".py"):
yield dirpath / name
这个写法有两个可量化的观察点:被排除的目录不会进入后续目录元组,保留下来的文件才会产生结果。它没有改变文件名的排序,也没有把目录过滤变成安全边界;如果目录在遍历期间被外部进程修改,结果仍应按文件系统的实际状态理解。
用 on_error 和 follow_symlinks 设定边界
默认情况下,扫描目录时产生的错误会被忽略。需要把权限不足、目录消失等情况记下来时,可以传入一个接收 OSError 的回调。回调可以记录后继续,也可以重新抛出,让调用方明确知道遍历没有完整完成。
from pathlib import Path
def report_scan_error(error: OSError) -> None:
# filename 可能存在;记录异常类型和路径,避免把失败伪装成空目录。
print("扫描失败:", type(error).__name__, getattr(error, "filename", None))
def walk_without_link_following(root: Path):
for dirpath, dirnames, filenames in root.walk(
on_error=report_scan_error,
follow_symlinks=False,
):
# 默认不跟随目录符号链接,避免把链接目标误当作普通子目录。
yield dirpath, dirnames, filenames
follow_symlinks=False 时,指向目录的符号链接不会按普通目录继续深入;打开它后,链接目标可能进入目录集合。官方文档特别提醒,若链接指向父目录,跟随链接可能形成无限递归,Path.walk() 不会替你记录已访问目录。因此,除非有明确的目录拓扑约束,否则保留默认值更稳妥。

根据任务选择遍历方向
需要提前排除目录时使用默认的自顶向下遍历。需要在处理父目录前先完成子目录汇总、删除或清理时,可以使用 top_down=False。但在自底向上模式下,目录列表已经生成,修改 dirnames 不会再阻止子目录被访问,所以不要把两种方向的控制语义混在一起。
| 目标 | 建议 | 原因 |
|---|---|---|
| 排除缓存目录 | top_down=True + 原地修改 dirnames | 在继续深入前裁剪目录集合 |
| 统计每个目录的子目录结果 | top_down=False | 子目录先产出,父目录可以后处理 |
| 遇到权限错误仍继续 | 传入记录型 on_error | 保留错误信息并继续其他目录 |
| 目录含未知符号链接 | 保持 follow_symlinks=False | 降低循环和重复遍历风险 |
如果业务要求“所有目录都必须成功扫描”,回调里不要只打印后继续,而应重新抛出或把错误汇总后返回失败状态。目录数量和文件数量可以作为结果摘要,但不能因为计数非零就推断所有目录均已访问。
常见问题
为什么修改 dirnames 后仍然进入了目录?
确认使用的是默认的 top_down=True,并且是对原列表做切片赋值,例如 dirnames[:] = ...。如果使用 top_down=False,子目录在当前元组产生前已经安排好,修改列表不会改变遍历行为。
Path.walk 返回的是完整路径吗?
dirpath 是当前目录的 Path,但 dirnames 和 filenames 里的元素是名称字符串。要打开文件或继续处理,应写成 dirpath / name。
on_error 里能不能直接忽略所有异常?
可以选择记录后继续,但这意味着结果可能是不完整的。若权限错误或目录丢失会影响业务结论,应让 OSError 继续传播,或把失败路径写入最终汇总。
什么时候可以使用 follow_symlinks=True?
只有在你明确知道链接目标、允许重复访问,并能接受目录环风险时才考虑。普通文件扫描、代码仓库统计和清理任务通常保持默认的 False 更容易解释。
-
137 收藏
-
446 收藏
-
337 收藏
-
161 收藏
-
218 收藏
-
115 收藏
-
254 收藏
-
320 收藏
-
118 收藏
-
196 收藏
-
文章 · python教程 | 1小时前 | 异常处理 · python · 异步编程 · asyncio · 超时处理 CancelledError Python异步 asyncio.timeout TimeoutError433 收藏
-
396 收藏
-
- 前端进阶之JavaScript设计模式
- 设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
- 立即学习 543次学习
-
- GO语言核心编程课程
- 本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
- 立即学习 516次学习
-
- 简单聊聊mysql8与网络通信
- 如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
- 立即学习 500次学习
-
- JavaScript正则表达式基础与实战
- 在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
- 立即学习 487次学习
-
- 从零制作响应式网站—Grid布局
- 本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
- 立即学习 485次学习