os.walk() 无法直接限制递归深度,需手动截断:以起始路径为第0层,用 root.count(os.sep) - start_depth 计算当前深度,topdown=True 时若≥目标深度则清空 dirs;pathlib.Path.rglob() 配合 relative_to().parts 过滤更简洁但非提前终止。

用 os.walk() 控制递归深度最直接
os.walk() 本身不支持深度限制,但可以手动截断。关键是在遍历过程中检查当前路径的层级,并在达到目标深度时跳过子目录。层级计算建议用 pathlib.Path(root).relative_to(start_path).parts 或更轻量的 root.count(os.sep) - start_depth(需注意 Windows 路径分隔符)。
常见错误是把深度理解成“进入几层子目录”,实际应以起始路径为第 0 层,其直接子目录为第 1 层。若从 /a 开始搜索、要求深度为 2,则只处理 /a/b 和 /a/b/c,不进 /a/b/c/d。
- 用
topdown=True(默认),才能在进入某目录前修改dirs列表来跳过子目录 - 每次迭代前计算当前
root相对于起始路径的深度,若 ≥ 目标深度,清空dirs列表 - 避免用
os.path.relpath(),它在符号链接或跨挂载点时可能出错;优先用count(os.sep)配合标准化路径
用 pathlib.Path.rglob() + 路径长度过滤更简洁
pathlib.Path.rglob("*.py") 会无深度限制地递归,但你可以对结果按路径层级过滤。比 os.walk() 少写循环逻辑,适合脚本快速实现,但需全部扫描完再过滤,内存和性能不如提前截断。
层级计算要小心:Windows 下 str(p).count("\") 不可靠(驱动器名含冒号),统一用 p.relative_to(start).parts 最稳妥,前提是 p 确实是 start 的子路径(可用 start in p.parents 预检)。
- 先调用
start_path.rglob("*"),再用if len(p.relative_to(start_path).parts) 筛选 - 注意
rglob("*")包含目录,加.is_file()才得文件 - 若
max_depth == 0,只返回start_path自身(需单独判断)
遇到符号链接时默认不跟随,需显式处理
os.walk() 默认跳过符号链接指向的目录(follow_symlinks=False),pathlib.rglob() 同样不解析符号链接目标。如果需求是“逻辑上”的深度(即把符号链接当作普通目录展开),必须手动处理——但这极易引发循环引用或无限遍历。
- 用
os.walk(..., follow_symlinks=True)有风险,建议配合seen = set()记录已访问的os.stat(path).st_ino和st_dev -
pathlib没有内置的 follow_symlinks 选项,需自己用resolve(strict=False)并去重 - 绝大多数场景应保持默认不跟随,否则深度控制失效(一个链接可能跳转到任意深层路径)
Python 3.11 的 os.walk() 行为没变,但注意新错误类型
Python 3.11 对 os.walk() 的底层行为未改动,但如果你在遍历中修改了正在访问的目录(如删除子目录),可能触发 FileNotFoundError 或 OSError。3.11 更倾向抛出具体异常而非泛化 OSError,比如 NotADirectoryError 会更早暴露。
- 不要在
os.walk()循环体内直接shutil.rmtree()当前root,会导致后续dirs迭代失败 - 若需边搜边删,先收集目标路径列表,循环结束后统一处理
- 捕获异常时,优先检查
isinstance(e, (PermissionError, FileNotFoundError)),而不是宽泛的Exception
深度控制真正的复杂点不在代码行数,而在于“深度”定义是否包含起始目录、是否计入符号链接、以及路径规范化是否一致。哪怕只差一层,结果就可能漏掉整个分支。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











