python禁止在async def中混用yield和await,因语法层级严格区分同步/异步生成器;yield在async def中仅表示异步生成器产出点,右侧须为普通表达式,异步计算需先await再yield。

async def 里写 yield await 会直接 SyntaxError
Python 明确禁止在同一个 async def 函数中把 yield 和 await 当成平级语句混用,比如 yield await self.ainvoke() 这种写法——它不是逻辑错,是语法非法,解释器会在解析阶段就报 SyntaxError: 'yield' inside async function。
根本原因在于:Python 的语法层级严格区分了「同步生成器」和「异步生成器」两种范式。async def 声明的函数,要么是协程(返回 coroutine 对象),要么是异步生成器(内部只能用 yield,不能用 yield await);它不接受“一半生成、一半等待”的中间态。
-
async def f(): yield 1→ 合法,返回异步生成器对象(async_generator) -
async def f(): await asyncio.sleep(1)→ 合法,返回协程对象 -
async def f(): yield await asyncio.sleep(1)→ ❌ 语法错误,不可编译
yield 在 async def 中只有一种合法身份:异步生成器的产出点
当你在 async def 函数里写 yield x,这个函数就不再是普通协程,而是一个异步生成器(async_generator)。此时 yield 本身不挂起、不等待,它只是把值交出去,控制权交还给调用方(通常是 async for 循环);真正可能挂起的是 yield 前后的 await 表达式。
典型正确模式是:
async def fetch_stream():
async for chunk in aiohttp_session.get(url):
data = await parse_chunk(chunk) # 这里 await 是合法的
yield data # 这里 yield 也是合法的,但不能写成 yield await ...
-
yield必须单独成句,右边只能是普通表达式(如变量、字面量、同步函数调用) - 如果右边需要异步计算,必须先
await拿到结果,再yield那个结果 -
async for item in gen才能消费这种函数,不能用for或next()
想边 await 边 yield?别用 yield await,改用 async for + yield
常见需求是“请求一批数据 → 解析一个 → 立即 yield 一个”,这时候容易下意识想写 yield await fetch_one()。但这是错的路径。正确做法是让被调用方返回异步迭代器,然后在外层用 async for 接收并转发:
async def stream_results():
async for raw in self.ainvoke_batch(): # 假设 ainvoke_batch 返回 async iterator
parsed = await self.parse(raw)
yield parsed
-
self.ainvoke_batch()必须返回支持__aiter__的对象(如AsyncGenerator、AsyncIterator) - 不能用
await self.ainvoke_batch()再对结果yield,那会等全部完成才开始 yield,失去流式意义 - 如果原方法只返回单个
coroutine(比如ainvoke()),那就无法直接流式 yield,得重构接口或用asyncio.create_task()并发拉取
yield from 和 await 完全不兼容,别试图互相替代
有人想绕过限制,把 await coro 换成 yield from coro,这会导致 TypeError: cannot 'yield from' a coroutine object。因为 yield from 只接受可迭代对象(Iterator),而 coroutine 不是迭代器,它没有 __iter__,只有 __await__。
-
await的目标是Awaitable(如coroutine、Future、带__await__的对象) -
yield from的目标是Iterable(如list、generator、带__iter__的对象) - 二者底层协议不同,强行混用既不会运行,也无法调试出预期行为
- Python 3.5+ 中,
yield from coro已被标记为反模式,连SyntaxWarning都会触发
最易被忽略的一点是:异步生成器的“异步性”来自外部驱动(async for 调用 __anext__),而不是 yield 本身。写的时候很容易误以为 yield 会暂停等待,其实它只是交出值,暂停与否取决于你有没有在它前后安排 await。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











