await只能在async函数中使用,因其是协程调度原语,依赖事件循环接管控制权;yield from是生成器委托语法,仅转发迭代,不触发异步等待,二者语义与接口契约完全不同,不可互换。

await 只能在 async 函数里用,yield from 不能直接替代它
很多人想用 yield from 模拟 await,结果报 SyntaxError: 'yield' inside async function 或运行时卡死。根本原因是:二者语义不同。await 是协程调度原语,依赖事件循环接管控制权;yield from 是生成器委托语法,只做迭代转发,不触发异步等待。
典型错误场景:把 async def 函数里一个 await asyncio.sleep(1) 换成 yield from asyncio.sleep(1) —— 直接语法报错,因为 asyncio.sleep() 返回的是 Future 对象,不是可迭代对象,yield from 根本无法消费它。
-
await后面必须是 awaitable(coroutine、Future、实现了__await__的对象) -
yield from后面必须是 iterator(或支持__iter__/__next__的对象) - 混用会导致类型错误或
TypeError: cannot 'yield from' a coroutine Object
yield from 在 asyncio 里基本没用武之地
除非你手动写生成器驱动的协程调度器(比如 python 3.4 以前的 @asyncio.coroutine + yield from 风格),否则在现代 Python(3.5+)中,yield from 和 async/await 属于两套不兼容的异步机制。
常见误解是“yield from 能让协程暂停”,但它只是把子生成器的产出逐个 yield 出来,并不移交事件循环控制权。真正让出 CPU 的是 await + 事件循环的 awaitable 对象(如 asyncio.sleep()、await reader.read(1024))。
立即学习“Python免费学习笔记(深入)”;
- Python 3.5 引入
async/await后,@asyncio.coroutine和yield from已被标记为 deprecated - 用
yield from包裹asyncio.sleep()或awaitable会抛TypeError - 想调试协程执行流?别用
yield from插桩,改用asyncio.create_task()+ 日志,或asyncio.current_task()
await 的实际行为取决于对象的 __await__ 方法
await 不是魔法,它只是调用对象的 __await__ 方法并迭代其返回的 iterator。这个 iterator 每次 yield 一个 Awaitable,直到结束,事件循环才继续推进当前协程。
所以你能 await 一个自定义类,只要它实现 __await__ 并返回有效 iterator:
class MyAwaitable: def __await__(self): yield # 让出一次控制权 return "done" <p>async def f(): result = await MyAwaitable() # 合法</p>
而 yield from 完全不关心 __await__,它只认 __iter__。这也是为什么不能互换——接口契约完全不同。
-
await x等价于x.__await__().__next__()(简化理解) -
yield from x等价于for i in x: yield i - 第三方库若返回
Future或coroutine,只能await,不能yield from
迁移旧代码时最容易漏掉的兼容性陷阱
把 @asyncio.coroutine + yield from 改成 async/await 时,最常踩的坑不是语法,而是隐式状态泄漏:旧风格里生成器帧对象可能保留局部变量引用,导致内存不释放;新风格下协程对象生命周期更清晰,但如果你在 async def 里意外用了 yield(非 yield from),函数就变成生成器了,调用它不会返回协程对象,而是生成器对象——然后你 await 它,立刻报 TypeError: object XXX can't be used in 'await' expression。
- 检查所有
async def函数体内是否混入了yield(哪怕只有一行) - 旧代码里
yield from asyncio.wait(...)必须改成await asyncio.wait(...),且注意asyncio.wait()返回值结构变了(现在返回(done, pending),不再是 generator) - 用
pylint或mypy开启await-never-in-async-def类检查项,能提前发现yield混用
异步的本质不在关键字,在控制流移交时机。写错一个 yield,整个协程就降级成同步生成器,而且错误往往延迟到 await 那一刻才暴露。