在这篇文章中,我们将深入探讨如何使用 Python 列出目录中包含特定扩展名的文件。虽然在 2026 年,AI 辅助编程已经成为主流,理解底层原理依然是构建高性能、健壮应用的基石。我们将从经典的 OS 模块和 Glob 模块入手,逐步深入到异步处理、工程化规范以及如何利用现代 AI 工具来优化这一看似简单的任务。
目录
使用的模块
在我们开始编写代码之前,让我们先快速回顾一下我们将要使用的核心工具。这些模块虽然历史悠久,但在 2026 年的 Python 生态中依然不可或缺,是构建复杂系统的原子组件。
- os:Python 中的 OS 模块提供了与操作系统进行交互的函数。它是我们操作文件系统的“瑞士军刀”,在处理跨平台路径拼接和权限检查时依然是首选。
- glob:在 Python 中,glob 模块用于检索符合指定模式的文件/路径名。glob 的模式规则遵循标准的 Unix 路径扩展规则。此外,根据最新的基准测试,它也被证明比在目录中手动循环匹配路径名更快,特别是在处理简单的模式匹配时。
使用的目录结构
为了确保我们讨论的一致性,让我们定义一个标准的目录结构作为演示基础。这模拟了一个典型的现代 Python 微服务项目的结构。
- root/home/project (根视图)
* documents (子目录)
* code (子目录)
* database_models (嵌套子目录)
schema_template.py*
sqlalchemy_models.py*
README.md*
requirements.txt*
main.py*
charter.xlsx*
timeline.jpg*
方法 1:使用 os 模块
该模块提供了一种使用依赖于操作系统功能的可移植方式。INLINECODEa11fa507 方法列出了目录中存在的所有文件。如果我们还想处理子目录,我们可以利用 INLINECODE7229c84c。虽然 os 模块比较底层,但它提供了极高的灵活性,尤其是在处理复杂的文件元数据时。
基础用法:os.listdir()
让我们先看一个简单的例子。os.listdir() 就像是我们用眼睛看文件夹一样,把里面所有的东西都列出来,不管它是文件还是子目录。
语法:
> os.listdir(path = ‘.‘)
返回一个包含给定路径下目录中条目名称的列表。
示例 1:列出特定路径下的内容并进行过滤
在这个例子中,我们不仅获取列表,还展示了一个初学者常犯的错误以及如何修正它。我们建议使用 os.path.splitext 来更准确地判断扩展名,而不是简单地检查字符串中是否包含点“.”。
import os
def list_files_with_extension_os(path, extension):
"""
使用 os 模块列出特定扩展名的文件。
注意:这种方法仅适用于当前目录,不递归子目录。
"""
try:
# 获取目录下的所有条目
all_entries = os.listdir(path)
# 我们使用列表推导式来过滤文件
# os.path.isfile 确保我们只选择文件,排除文件夹
# str.lower() 确保匹配大小写不敏感
target_files = [
entry for entry in all_entries
if os.path.isfile(os.path.join(path, entry)) and
entry.lower().endswith(extension.lower())
]
return target_files
except FileNotFoundError:
print(f"错误:路径 {path} 不存在")
return []
except Exception as e:
print(f"发生未知错误: {e}")
return []
# 模拟调用
# files = list_files_with_extension_os(r"root/home/project", ".jpg")
# print(files)
深入递归:os.walk()
当我们需要遍历一棵巨大的目录树时,os.walk() 是我们的首选。它就像是一个勤劳的矿工,一层一层地深入地下,把每一层找到的宝藏都列出来。对于 2026 年的日志分析系统,这种能力至关重要。
语法:
> os.walk(top, topdown=True, onerror=None, followlinks=False)
示例 2:递归遍历目录树
在我们最近的一个数据清洗项目中,我们需要处理数万个深嵌套在目录中的日志文件。os.walk() 的稳定性给我们留下了深刻印象。让我们看看如何优雅地实现它。
import os
def recursive_find_files(root_path, extension):
"""
使用 os.walk 递归查找所有具有特定扩展名的文件。
这是生产环境中处理深层目录结构的常用方法。
"""
matched_files = []
# os.walk 生成一个元组:
# 1. 当前路径
# 2. 当前路径下的子文件夹列表
# 3. 当前路径下的文件列表
for dirpath, dirnames, filenames in os.walk(root_path):
for filename in filenames:
# 使用 os.path.splitext 是更规范的做法
# 它返回,如 (‘filename‘, ‘.txt‘)
if filename.lower().endswith(extension.lower()):
# 使用 os.path.join 拼接路径,确保跨平台兼容性
full_path = os.path.join(dirpath, filename)
matched_files.append(full_path)
return matched_files
# 模拟调用:查找项目中所有的 Python 文件
# py_files = recursive_find_files("root/home/project", ".py")
# for f in py_files:
# print(f)
方法 2:使用 glob 模块
glob 模块根据 Unix shell 使用的规则查找所有匹配指定模式的路径名。它比 INLINECODE64202fb3 更高级,因为我们可以直接传递模式字符串,比如 INLINECODEc537b975。在 2026 年,由于大家对正则表达式的普遍熟悉,glob 的使用变得更加直观。
语法:
> glob.glob(pathname, *, recursive=False)
‘‘ 意味着它将匹配所有项目,类似于正则表达式中的 .*。
现代 Python 的首选:INLINECODE2ab03aef 与 INLINECODE18153bba
在 Python 3.10+ 的版本中(更不用说 2026 年的标准),INLINECODEd5139807 模块配合 INLINECODEfe80bd0b 已经成为最“Pythonic”的做法。它的可读性更强,代码量更少。
示例:使用 Glob 进行模式匹配
import glob
import os
def find_files_with_glob(pattern, root_path):
"""
使用 glob 模块查找文件。
支持 ** 递归通配符,这使得代码极其简洁。
"""
# 构建完整的搜索模式,例如: root/home/project/**/*.pdf
# recursive=True 允许 ** 匹配任意层级的子目录
search_pattern = os.path.join(root_path, pattern)
files = glob.glob(search_pattern, recursive=True)
return files
# 模拟调用:查找所有 PDF 文件,无论它在多深的子目录里
# pdfs = find_files_with_glob("**/*.pdf", "root/home/project")
# print(f"找到 {len(pdfs)} 个 PDF 文件。")
2026 年工程化视角:高性能与异步 I/O
虽然上述方法在脚本中运行良好,但当我们构建现代 AI 原生应用或处理大规模数据管道时,我们需要考虑更多的因素:性能、异步 I/O、安全性以及可维护性。
为什么我们需要异步?
在传统的同步 I/O 模型中,当我们读取一个包含 100,000 个文件的目录时,CPU 必须等待硬盘每一次寻址和读取操作的完成。这在处理网络文件系统(NFS)或云存储(如 AWS S3 挂载点)时尤为明显,会导致严重的性能瓶颈。
在 2026 年,随着数据量的爆炸式增长,并发不再是可选的优化,而是必需的标准。
使用 INLINECODEe1c43b6a 和 INLINECODE789fe942 进行异步遍历
我们可以引入 Python 的 INLINECODE17df7a92 和 INLINECODEd2581415 库来实现异步文件遍历。这允许我们在等待硬盘响应时,CPU 可以去处理其他任务(比如预处理已加载的数据或响应用户请求)。这在构建高并发的 FastAPI 或 Starlette 后端服务时是黄金标准。
示例:高性能异步文件扫描器
import os
import asyncio
import aiofiles.os as aios
async def async_list_files(root_path, extension):
"""
异步地列出目录中的文件。
这在构建高并发服务(如 FastAPI 后端)时非常有用。
注意:aiofiles 需要安装:pip install aiofiles
"""
matched_files = []
try:
# 异步遍历目录树
# 这里的 walk 是异步生成器,不会阻塞事件循环
async for dirpath, dirnames, filenames in aios.walk(root_path):
for filename in filenames:
if filename.lower().endswith(extension.lower()):
full_path = os.path.join(dirpath, filename)
matched_files.append(full_path)
# 模拟异步处理文件(例如:将路径发送到队列)
# await process_file_async(full_path)
except PermissionError:
print(f"警告: 没有权限访问 {root_path}")
except Exception as e:
print(f"异步扫描发生错误: {e}")
return matched_files
# 运行示例
# async def main():
# files = await async_list_files("root/home/project", ".py")
# print(f"异步找到 {len(files)} 个文件")
#
# asyncio.run(main())
决策框架:os vs glob vs pathlib
我们经常在团队代码评审中讨论这个问题:到底该用哪一个?这里是我们总结的 2026 年选型指南:
- 简单脚本: 如果你只是想快速列出一个文件夹里的图片,用
glob.glob("*.jpg")。它最快,最直观。 - 复杂逻辑与过滤: 如果你需要在遍历过程中进行复杂的判断(比如排除特定名字的文件夹,或者根据文件大小过滤),
os.walk()提供了最好的控制力。 - 现代项目与跨平台: 如果你在构建一个需要长期维护的大型应用,请使用
pathlib.Path.glob()。它是面向对象的,处理路径分隔符(Windows vs Linux)更加智能。
安全性与边界情况:生产环境的护城河
作为开发者,我们不仅要让代码跑通,还要让它安全。特别是在处理用户输入的路径时,一个小小的疏忽可能导致严重的安全漏洞。
1. 路径遍历攻击
如果用户输入 ../../etc/passwd 作为搜索路径,你的程序可能会意外泄露系统敏感信息。这是 2026 年依然常见的 OWASP 漏洞。
解决方案: 始终验证并清理输入路径,确保它在预期的根目录范围内。
import os
def safe_join(root_path, user_path):
"""
安全地拼接路径,防止路径遍历攻击。
"""
# 规范化路径,解析所有的 .. 和 .
full_path = os.path.normpath(os.path.join(root_path, user_path))
# 确保规范化后的路径依然以 root_path 开头
# os.path.commonprefix 可以用来检查前缀
if not os.path.commonpath([full_path, root_path]) == root_path:
raise ValueError("非法路径:试图访问根目录之外的区域")
return full_path
2. 符号链接循环
默认情况下,os.walk 可能会陷入符号链接的无限循环中(例如,A 链接到 B,B 又链接回 A)。这会导致程序挂起甚至耗尽文件描述符。
解决方案: 设置 followlinks=False(这是默认值),或者严格限制递归深度,并记录已访问的 inode。
3. 权限错误的优雅处理
在遍历系统目录时,频繁遇到 PermissionDenied 错误。不要让这些错误弄脏你的控制台输出。
解决方案: 使用 INLINECODE8819c0f3 块包裹文件操作,优雅地处理错误并使用 Python 的 INLINECODEe5a27f60 模块记录日志,而不是使用 print()。
AI 时代的开发:Agentic Workflow 与 Vibe Coding
在 2026 年,我们不再只是“写代码”,我们是在“设计系统”。AI 工具(如 Cursor, GitHub Copilot, Windsurf)已经成为了我们的结对编程伙伴。
如何与 AI 协作编写文件操作代码
当我们使用 AI 辅助编程时,提示词的质量直接决定了代码的质量。不要只说“写个脚本”。我们应该采用 Agentic Workflow(代理工作流) 的思维:
> 提示词示例:
> *"扮演一位高级 Python 架构师。请编写一个使用 pathlib 的异步函数,用于递归查找所有 Markdown 文件。请遵循以下约束:
> 1. 忽略任何名为 ‘venv‘ 或 ‘.git‘ 的目录。
> 2. 处理 PermissionError 异常并记录日志。
> 3. 返回一个 Path 对象列表,而不是字符串。
> 4. 添加类型注解以支持静态检查。”*
这种提示词不仅指定了方法(pathlib),还指定了排除规则、异常处理和类型安全,这体现了现代开发的Vibe Coding(氛围编程)理念——我们描述意图和边界,AI 处理实现细节,而我们需要理解底层原理来审查 AI 的输出。
总结
在这篇文章中,我们探讨了从基础的 os.listdir 到工程化的异步文件处理。虽然技术不断迭代,但理解文件系统的运作机制依然是我们驾驭 Python 的关键。
回顾一下,我们学到了:
- os.walk 提供了最强的控制力,适合复杂的文件处理逻辑。
- glob 和 pathlib 提供了最简洁、现代的语法,适合日常开发。
- asyncio 和 aiofiles 是解决 I/O 密集型任务性能瓶颈的关键。
- 安全性 始终是生产环境代码不可妥协的一环。
希望这些实战经验和技巧能帮助你在 2026 年写出更优雅、更高效、更安全的代码。如果你在项目中遇到特殊的文件处理难题,或者有更好的优化建议,欢迎在评论区与我们分享你的故事。