当前位置:首页 > 文章列表 > 文章 > python教程 > Path.walk 怎样控制目录遍历

Path.walk 怎样控制目录遍历

来源:17golang原创 2026-10-25 13:38:21 0浏览 收藏

如果要控制 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 交给后续后缀过滤,但不要直接假设列表已经排序。

Path.walk、dirnames、filenames 与遍历参数的静态关系说明图
图1:Path.walk 的目录控制结构说明图,查看 top_down 与 dirnames 如何影响目录集合;这是原创静态说明图,不是运行截图。

用 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() 不会替你记录已访问目录。因此,除非有明确的目录拓扑约束,否则保留默认值更稳妥。

Path.walk 的 on_error、OSError、follow_symlinks 与遍历方向边界说明图
图2:Path.walk 的错误与链接边界说明图,查看 on_error、follow_symlinks 和自底向上配置的职责;这是原创静态说明图,不是运行截图。

根据任务选择遍历方向

需要提前排除目录时使用默认的自顶向下遍历。需要在处理父目录前先完成子目录汇总、删除或清理时,可以使用 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 更容易解释。

版本声明
本文转载于:17golang原创 如有侵犯,请联系study_golang@163.com删除
asyncio.timeout 怎样转换超时异常asyncio.timeout 怎样转换超时异常
上一篇
asyncio.timeout 怎样转换超时异常
tomllib 怎样读取 TOML 配置
下一篇
tomllib 怎样读取 TOML 配置
查看更多
最新文章
查看更多
课程推荐
  • 前端进阶之JavaScript设计模式
    前端进阶之JavaScript设计模式
    设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
    543次学习
  • GO语言核心编程课程
    GO语言核心编程课程
    本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
    516次学习
  • 简单聊聊mysql8与网络通信
    简单聊聊mysql8与网络通信
    如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
    500次学习
  • JavaScript正则表达式基础与实战
    JavaScript正则表达式基础与实战
    在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
    487次学习
  • 从零制作响应式网站—Grid布局
    从零制作响应式网站—Grid布局
    本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
    485次学习
查看更多
AI推荐
  • PubMedQA数据集详解:生物医学问答基准、功能与应用指南
    PubMedQA
    深入了解PubMedQA生物医学问答数据集,涵盖其核心功能、使用方法及在临床决策、药物研发等场景的应用,助力提升NLP模型性能。
    685次使用
  • H2O EvalGPT:开源LLM大模型评估与排行榜工具
    H2O EvalGPT
    H2O EvalGPT是H2O.ai推出的开源LLM评估平台,提供详细的大模型性能排行榜、行业特定基准测试及A/B测试功能,助您快速选择最适合项目的高性能大语言模型。
    700次使用
  • LMArena是什么?伯克利AI模型评估平台使用指南与功能解析
    LMArena
    LMArena是加州大学伯克利分校推出的AI模型匿名评测平台。通过盲测投票机制,用户可对比不同大模型回答并生成实时排行榜,助力开发者优化模型及用户选择最佳AI工具。
    753次使用
  • 斯坦福HELM:大语言模型Holistic Evaluation整体评估框架详解
    HELM
    深入了解斯坦福推出的HELM(Holistic Evaluation of Language Models)大模型评测体系。本文解析其核心功能、安装配置步骤及应用场景,涵盖准确性、公平性、鲁棒性等多维度指标,助力开发者全面优化语言模型性能。
    717次使用
  • MMBench详解:多模态大模型基准测试、功能特点与使用指南
    MMBench
    MMBench是由上海人工智能实验室等机构联合推出的多模态基准测试平台,提供细粒度能力评估、大规模数据集及VLMEvalKit工具。本文详细介绍其核心功能、安装使用方法及应用场景,助力开发者全面评估多模态模型性能。
    552次使用
微信登录更方便
  • 密码登录
  • 注册账号
登录即同意 用户协议 和 隐私政策
返回登录
  • 重置密码