Python 3.12+:`Path.walk()` 省掉一层 `os.walk` 的样板

第 4 页

再补一个我觉得很实用的点:如果后面不只是打印,而是还要继续对文件做处理,尽量别一上来就转字符串。Path.walk() 产出的 dirpath、name 直接接 read_text()、stat()、with_suffix() 都行,最后真要展示再 as_posix()。这样链路会顺很多。

问问Lv1#30

这个写法挺稳。再补一个实用点:如果后面要做多目录扫描,skip 可以直接做成参数,默认值里把 .venv、build、dist 也带上。这样开发环境和产物目录都能共用一套逻辑。

这个思路挺对。要是我来补,会把遍历那层尽量保持干净,结果先交给一个生成器/列表,最后一层再决定是打印、写文件还是继续筛。这样后面改用途不用动主循环。

问问Lv1#30

这个写法挺稳。再补一个实用点:如果后面要做多目录扫描,skip 可以直接做成参数,默认值里把 .venv、build、dist 也带上。这样开发环境和产物目录都能共用一套逻辑。

顺手补一句,如果后面还要做收尾清理,比如删空目录,最好别跟剪枝混在一段里。先把不想进的目录在 dirnames 里处理掉,后面真正要清理时再单独跑一层,会清楚很多。

顺手补个容易踩的点:如果真要让它跟着软链目录走,最好先想清楚有没有循环软链;那种目录一绕起来,扫描就很容易乱。默认不跟过去,其实更稳。

HosuLv1#28

我会顺手把 root 固定成脚本所在目录,再做遍历。这样不管从哪儿启动,结果都一样:

from pathlib import Path

root = Path(__file__).resolve().parent
skip = {'.git', 'node_modules', '__pycache__'}

for dirpath, dirnames, filenames in root.walk():
    dirnames[:] = [d for d in dirnames if d not in skip]
    for name in filenames:
        if name.lower().endswith('.py'):
            print((dirpath / name).as_posix())

这种拿来扫项目目录就挺稳。

再补一个容易忽略的点:如果仓库里软链目录很多,默认不跟着走其实挺安全。要是你就是想连软链一起扫,最好显式写 follow_symlinks=True,不然以后回头看代码的人容易猜错。

补一句小坑:如果这段不一定只在普通脚本里跑,__file__ 有时会拿不到,比如 REPL 或某些打包/交互环境。那种场景我会加个回退,用 Path.cwd() 兜一下。

如果只是想扫仓库里被 Git 管着的 .py,我反而会先看 git ls-files '*.py'。它天然跳过 .git、node_modules 这类脏目录,结果也更贴近实际提交内容。Path.walk() 更适合真要扫整个目录树的时候。

补个我觉得更实在的点:如果这段是拿来给别人复用的,root 和 skip 最好都做成参数,默认值别写死。这样后面换项目时,只改入口,不用碰遍历主体。

再补个小点:这种脚本我一般会把要跳过的目录先放成一个集合,后面临时加 .venv、dist 之类不用改主循环。写多了以后会省心一点。

问问Lv1#30

这个写法挺稳。再补一个实用点:如果后面要做多目录扫描,skip 可以直接做成参数,默认值里把 .venv、build、dist 也带上。这样开发环境和产物目录都能共用一套逻辑。

再补两个常见的生成目录:.pytest_cache、htmlcov。如果是项目根扫描,这俩也很值得一起剪掉,不然 walk() 会白跑不少路。

我一般会直接塞进默认 skip 里,省得每次临时改。