async with 不能直接用普通上下文管理器,因为其要求 aenter 和 aexit 为异步方法并用 await 调用,而同步的 enter__/__exit 会触发 TypeError;二者签名、返回值及使用场景均不同,且需确保底层 IO 真异步。

async with 为什么不能直接用普通上下文管理器
因为 __aenter__ 和 __aexit__ 是异步方法,必须用 await 调用;而普通 __enter__/__exit__ 是同步的,扔进 async with 会直接报 TypeError: object X is not async context manager。
常见错误现象:
- 把已有的同步数据库连接类(含
__enter__)直接丢进async with块里 - 忘了在
__aenter__里加return await ...,只写了return ...,导致返回协程对象而非实际资源
__aenter__ 和 __aexit__ 的签名与返回值要求
这两个方法必须是 async def,且 __aenter__ 返回的是你想要的资源实例(不是协程),__aexit__ 必须返回 bool 或 None(用于抑制异常)。
实操建议:
立即学习“Python免费学习笔记(深入)”;
-
__aenter__中所有耗时操作(如网络连接、认证)都得用await,最后return self或return resource -
__aexit__参数顺序固定:(self, exc_type, exc_value, traceback);若要吞掉异常,结尾写return True;否则别写return,让默认的None表示不抑制 - 不要在
__aexit__里await后续清理失败就静默——这会让错误消失得毫无痕迹
示例片段:
async def __aenter__(self):
await self._connect() # 必须 await
return self
<p>async def <strong>aexit</strong>(self, exc_type, exc_value, traceback):
await self._close() # 清理也要 await</p><h1>不 return → 异常正常传播async with 嵌套或与普通 with 混用时的陷阱
不能交叉混用:比如在 async with 块里写 with open(...) 没问题,但反过来——在普通 with 里写 async with——会触发 SyntaxError,因为 async with 只能在 async def 函数内使用。
使用场景注意点:
- HTTP 客户端(如
aiohttp.ClientSession)、异步数据库驱动(如asyncpg.Pool)都实现了这套协议,直接用即可 - 自己封装时,别为了“看起来支持 async”而在
__aenter__里只写return self却没真正做异步初始化——这会导致资源未就绪就被使用 - 如果类同时想支持
with和async with,得分别实现两套方法,且逻辑不能共用同步/异步 IO 调用
性能与兼容性:async with 不等于“更快”,而是“不阻塞事件循环”
它本身不提速,只是把阻塞点(如网络等待、磁盘读取)从主线程移出,交由事件循环调度。如果底层 IO 操作没用 async 库(比如调了 requests.get),那 async with 包裹也没用,照样卡死整个协程。
容易被忽略的地方:
- 第三方库是否真异步?查文档看它依赖的是
aiohttp还是requests,看源码里有没有await调用底层 socket -
__aexit__抛异常时,不会自动回滚事务——异步数据库的事务控制仍需手动await conn.rollback(),不能指望上下文管理器代劳 - Python 3.6+ 才支持
async with,低于此版本会语法报错,不是运行时报错
事情说清了就结束









