async with 的 aenter__/__aexit 必须用 async def 定义并返回 Awaitable,不可用 yield from(会生成 AsyncGenerator 而违反协议);应改用 await 调用普通协程。

Python 3.7+ 中的异步上下文管理器(async with)**不支持直接在 __aenter__ 或 __aexit__ 中使用 yield from**,因为这两个方法必须返回 Awaitable,而非生成器。你真正需要的是:用 async def 定义它们,并在内部 await 其他协程——而不是试图“委托”给另一个异步生成器。
为什么 yield from 在 __aenter__/__aexit__ 中会报错
异步上下文管理器协议要求:
-
__aenter__必须是async def函数,返回一个可等待对象(如await后得到的结果) -
__aexit__同样必须是async def,返回None或一个真值(用于抑制异常) -
yield from出现在async def中,会使函数变成异步生成器(返回AsyncGenerator),违反协议
常见错误现象:SyntaxError: 'yield from' inside async function(Python 3.6+ 默认禁止),或运行时报 TypeError: __aenter__ returned non-awaiter。
正确写法:用 await 替代 yield from 委托协程
如果你原本想用 yield from some_async_iter() 来复用一段异步初始化/清理逻辑,实际应把那段逻辑封装为普通协程函数,然后在 __aenter__/__aexit__ 中 await 它。
import asyncioasync def do_setup(): await asyncio.sleep(0.1) return "resource"
async def do_cleanup(res): await asyncio.sleep(0.1) print(f"cleaned {res}")
class AsyncResource: def init(self): self._res = None
async def __aenter__(self): # ✅ 正确:await 协程,不是 yield from self._res = await do_setup() return self._res async def __aexit__(self, exc_type, exc_val, exc_tb): if self._res is not None: await do_cleanup(self._res)使用方式不变:
async with AsyncResource() as res:。
极品模板多语言企业网站管理系统1.2.2下载【极品模板】出品的一款功能强大、安全性高、调用简单、扩展灵活的响应式多语言企业网站管理系统。 产品主要功能如下: 01、支持多语言扩展(独立内容表,可一键复制中文版数据) 02、支持一键修改后台路径; 03、杜绝常见弱口令,内置多种参数过滤、有效防范常见XSS; 04、支持文件分片上传功能,实现大文件轻松上传; 05、支持一键获取微信公众号文章(保存文章的图片到本地服务器); 06、支持一键
如果必须复用异步生成器逻辑,需手动展开
假设你有一个异步生成器
async def setup_steps(),想把它“展开”进__aenter__。不能yield from,但可以显式迭代并await每个anext():async def setup_steps(): await asyncio.sleep(0.05) yield "step1" await asyncio.sleep(0.05) yield "step2"class AsyncResourceWithSteps: async def aenter(self): agen = setup_steps() try:
手动取第一个值(模拟“进入”时完成初始化)
self._step = await anext(agen) return self._step except StopAsyncIteration: raise RuntimeError("setup_steps yielded no values") async def __aexit__(self, *args): # 注意:无法自动继续迭代剩余步骤 —— 异步生成器状态已丢失 # 若需完整流程,应在 enter 中一次性 await 全部,或改用普通协程 pass⚠️ 这种写法脆弱:异步生成器无法跨
__aenter__/__aexit__恢复状态;__aexit__无法访问同一个agen实例。实际项目中应避免。最易被忽略的一点:异步上下文管理器的生命周期是单次、线性的,它不提供“暂停-恢复”能力;所谓“委托”,本质是组合协程,不是委托生成器。别让
yield from的直觉误导你去写语法非法或语义断裂的代码。










