glob.glob()默认不递归,需显式设recursive=True并配合通配符(如"/*.py");pathlib.Path.rglob()更直观,默认递归且返回Path对象。

glob 通配符在多级目录中默认不递归
Python 标准库 glob 模块的 glob.glob() 默认只匹配**当前层级**的文件,即使写成 "**/*.py",在 Python 3.4 之前根本不会生效——它会被当作字面量字符串处理,不识别 ** 语法。
从 Python 3.5 开始,glob 才支持 recursive=True 参数启用递归匹配,但必须显式传入,且 ** 必须紧挨着 /(如 "**/*.txt" 合法,"**.txt" 不合法)。
- 错误写法:
glob.glob("src/**/*.py")(没加recursive=True,等同于找字面名为**的子目录) - 正确写法:
glob.glob("src/**/*.py", recursive=True) - 注意:
**只能出现在路径段中,不能单独用或放在末尾(如"src/**"不会报错但可能匹配不到预期内容)
替代方案:pathlib.Path.rglob() 更直观可靠
如果你用的是 Python 3.4+,pathlib 是更现代、更少出错的选择。Path.rglob() 默认就是递归的,语法也更贴近直觉:
from pathlib import Path
py_files = list(Path("src").rglob("*.py"))
它自动跳过权限不足的目录(静默失败),返回 Path 对象而非字符串,后续调用 .stem、.suffix、.resolve() 都很自然。
立即学习“Python免费学习笔记(深入)”;
- 对比
glob:不用记recursive=True,也不用担心**位置 - 注意:如果目标目录不存在,
Path("xxx").rglob(...)不会报错,但返回空迭代器;而glob.glob("xxx/**/*.py", recursive=True)会直接返回空列表,行为一致但调试时容易误以为路径写错了 - 性能上两者差异不大,但
pathlib在组合路径(如subdir / "config.json")时可读性明显更好
需要过滤软链接或排除特定目录?用 os.walk + fnmatch 更灵活
当你要跳过 __pycache__、.git 或只遍历符号链接指向的真实目录时,glob 和 pathlib.rglob() 都不够用。这时应切换到 os.walk() 配合 fnmatch.filter():
import os
import fnmatch
<p>def find_py_files(root):
py_files = []
for dirpath, dirnames, filenames in os.walk(root):</p><h1>修改 dirnames 原地剔除不想进入的目录(注意:必须原地修改!)</h1><pre class="brush:php;toolbar:false;"> dirnames[:] = [d for d in dirnames if d not in {".git", "__pycache__"}]
matches = fnmatch.filter(filenames, "*.py")
py_files.extend(os.path.join(dirpath, f) for f in matches)
return py_files
-
os.walk()天然支持控制遍历逻辑,比如跳过某些目录、按深度截断、区分文件/目录权限 -
fnmatch.filter()比str.endswith()更准(支持"*.py?"、"[a-z]*.py"等 shell 风格模式) - 陷阱:别写
dirnames = [...]—— 这只是重新赋值局部变量,不影响os.walk()下一步遍历;必须用dirnames[:] = [...]原地替换
Windows 路径分隔符和大小写问题要手动处理
glob 和 pathlib 在 Windows 上返回的路径使用反斜杠 \,但多数工具(如 open()、subprocess)能兼容;若需跨平台稳定,建议统一转为正斜杠或用 Path.as_posix():
for p in Path("src").rglob("*.md"):
print(p.as_posix()) # 输出 src/docs/intro.md,不是 src\docs\intro.md
另外,Windows 文件系统默认不区分大小写,但 glob 和 pathlib 的模式匹配是区分大小写的(例如 "*.PY" 不会匹配 main.py)。如需忽略大小写,得自己封装:
- 简单方案:用
path.suffix.lower() == ".py"替代通配符 - 严谨方案:用
re.fullmatch(r".*\.(py|PY|Py)$", str(path)) - 别依赖
glob自动做大小写归一化——它不做
多级匹配本身不难,难的是边界情况:空目录、权限拒绝、符号链接循环、大小写混用、路径编码异常。选对工具只是第一步,关键看你怎么兜住这些漏网之鱼。


















