asyncio 本身不支持文件系统事件监听,必须使用 watchfiles 等专用异步库;watchfiles.watch() 提供事件驱动的异步生成器接口,需用 async for 遍历且事件处理必须非阻塞,否则会丢事件。

asyncio 本身不支持文件系统事件监听
Python 的 asyncio 标准库没有内置的文件变动监听能力,它只管协程调度和 I/O 复用,不碰 inotify / kqueue / ReadDirectoryChangesW 这类系统级文件监控接口。硬套 asyncio.to_thread() 或 loop.run_in_executor() 虽然能跑,但会丢失事件实时性、增加延迟,还可能漏事件——尤其在高频写入场景下。
真正可行的路只有一条:用专门的异步文件监控库,底层绑定原生事件机制,再暴露成 awaitable 接口。
- Linux/macOS 推荐
watchfiles(纯 Python 封装,依赖watchdog的 C 扩展或inotify原生调用) - Windows 下
watchfiles同样可用,它会自动 fallback 到ReadDirectoryChangesW - 避免直接用
watchdog+AsyncIOEventEmitter,它的异步支持是模拟的,实际仍是线程+回调,和 asyncio event loop 不完全对齐
watchfiles.watch() 是最简可用的异步入口
watchfiles.watch() 返回一个异步生成器,每次 yield 一个 Change 元组列表,结构为 (change_type, file_path),其中 change_type 是 watchfiles.Change.added、.modified、.deleted 等枚举值。
快速生成专业的 Python 脚本和应用代码。一键创建完整项目结构,支持CLI、API、爬虫、Bot、Django等多种项目类型,包含完整的项目结构、配置文件、依赖管理、测试、README和文档。
它默认阻塞式轮询?不是。底层用的是 inotify/kqueue/ReadDirectoryChangesW,零轮询,纯事件驱动,CPU 占用极低。
- 必须用
async for遍历,不能用普通for - 路径参数支持 glob 模式,比如
"./logs/*.log",但注意:递归监控需显式加recursive=True - 首次启动时不会触发“已存在文件”的
added事件,这是设计行为,不是 bug - 若要忽略某些路径,用
filter=lambda p: "temp" not in str(p),别用ignore参数——它只过滤错误,不过滤事件
import asyncio
from watchfiles import watch
async def main():
async for changes in watch("./src", recursive=True):
for change_type, file_path in changes:
if change_type == "modified":
print(f"检测到修改: {file_path}")
asyncio.run(main())
事件处理中别 await 耗时操作,否则压垮事件队列
watchfiles.watch() 内部有事件缓冲区,如果你在 async for 循环里直接 await asyncio.sleep(5) 或调用 httpx.post(),新事件会持续积压,直到缓冲区满(默认 4096 条),然后旧事件被丢弃——你看到的就是“断连式”监控,中间一堆变动消失了。
- 所有耗时逻辑必须扔进后台任务:
asyncio.create_task(handle_change(change)) - 如果需要顺序处理同一文件的多次变更(比如防抖),得自己加
asyncio.Lock或用asyncio.Queue做节流,watchfiles不提供去重或合并功能 - 不要在循环里打开文件读内容——小文件可以,大文件会阻塞事件循环;改用
asyncio.to_thread(open, path, "rb")包一层 - 异常必须捕获,否则整个
async for会中断退出,监控就停了
Windows 下路径大小写和符号链接容易出错
Windows 文件系统默认不区分大小写,但 watchfiles 的路径匹配是字面量比对。如果你监控 "./Config",而实际被修改的是 ./config/config.yaml,事件会被静默忽略。
- 统一用
pathlib.Path().resolve()规范路径,再传给watch() - 符号链接默认不跟随,想监控目标目录内容,得加
follow_symlinks=True,但 Windows 上这选项依赖管理员权限,否则抛PermissionError - 长路径(>260 字符)在 Windows 默认被禁用,需提前在注册表启用
LongPathsEnabled,或用\?前缀——watchfiles不自动处理这个 - 杀毒软件(尤其是 McAfee、Symantec)会 hook 文件操作,导致事件延迟甚至丢失,测试时建议临时关闭
watchfiles.watch() 而不是自己封装、事件处理必须非阻塞。其余都是路径规范和平台细节问题。Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!










