Python 3.12:用 Path.walk 遍历目录,少写一层 os.walk 适配

41 次浏览4 条回复

Python 3.12 的 pathlib.Path 增加了 walk(),遍历目录时可以直接从 Path 对象开始,返回的目录和文件名也更容易继续拼路径。

环境:Python 3.12+。建个最小目录试试:

mkdir -p path-walk-demo/src path-walk-demo/cache
printf 'print(1)\n' > path-walk-demo/src/app.py
printf 'tmp\n' > path-walk-demo/cache/data.tmp
printf 'readme\n' > path-walk-demo/README.md
cat > path-walk-demo/list_py.py <<'PY'
from pathlib import Path

root = Path(__file__).parent
for current, dirs, files in root.walk():
    dirs[:] = [name for name in dirs if name != "cache"]
    for name in files:
        path = current / name
        if path.suffix == ".py":
            print(path.relative_to(root))
PY

cd path-walk-demo
python list_py.py

输出会包含 list_py.py 和 src/app.py,但不会进入 cache。dirs 是当前层待继续遍历的目录名列表,提前改它就能剪枝。

如果项目还要兼容 Python 3.11,就继续用 os.walk() 或自己封装一层,别只因为写法更顺手就直接抬高版本要求。

这里还有个小细节:current 是 Path,但 dirs 和 files 里面还是字符串名,不是完整 Path。

所以要做后续判断时一般还是写成:

path = current / name

另外如果是扫用户上传目录,最好顺手传个 on_error,不然遇到权限问题时排查起来有点闷。比如先打印一下哪个目录打不开,再决定要不要继续跳过。

再补一个容易忽略的点:Path.walk() 默认不会跟随符号链接目录,follow_symlinks=True 要谨慎开,目录互链时可能反复走;而且它不像 Path.rglob() 那样直接给出完整路径列表。做清理或索引时保留 current / name 这种写法,控制会更明确。

做清理类操作时还可以注意 top_down=False:它会先遍历子目录,再回到父目录,删空目录时更顺手。这个模式下修改 dirs 已经不会影响遍历顺序,剪枝还是用默认的 top_down=True。

from pathlib import Path

root = Path("./cache")
for current, dirs, files in root.walk(top_down=False):
    for name in files:
        (current / name).unlink()
    for name in dirs:
        (current / name).rmdir()

实际项目里我会再加异常处理,避免目录刚好被其他进程改动时整段中断。

别熬夜啦Lv1#3

做清理类操作时还可以注意 top_down=False:它会先遍历子目录,再回到父目录,删空目录时更顺手。这个模式下修改 dirs 已经不会影响遍历顺序,剪枝还是用默认的 top_down=True。

from pathlib import Path

root = Path("./cache")
for current, dirs, files in root.walk(top_down=False):
    for name in files:
        (current / name).unlink()
    for name in dirs:
        (current / name).rmdir()

实际项目里我会再加异常处理,避免目录刚好被其他进程改动时整段中断。

top_down=False 这个补充很实用。清理脚本里我会再把 unlink() 和 rmdir() 分开处理 FileNotFoundError,目录被并发任务先删掉时可以继续扫,不至于整段退出。符号链接也最好单独判断,避免把链接目标当成普通文件处理。