
本文详解如何在 python 异步编程中安全构建字典,重点解决因未正确 await asyncio.gather 返回的 future 导致的 runtimeerror,并提供两种推荐写法及底层原理说明。
本文详解如何在 python 异步编程中安全构建字典,重点解决因未正确 await asyncio.gather 返回的 future 导致的 runtimeerror,并提供两种推荐写法及底层原理说明。
在 Python 异步开发中,asyncio.gather() 是并发执行多个协程并收集结果的常用工具。但许多开发者在尝试将其与字典推导(dict comprehension)结合时,会遇到如下典型错误:
RuntimeError: await wasn't used with future sys:1: RuntimeWarning: coroutine 'div7_tuple' was never awaited
该错误的根本原因在于:asyncio.gather() 返回的是一个 Future 对象,而非已完成的结果序列;而字典推导式 {k: v for k, v in ...} 会直接尝试迭代该 Future,却未先 await 它使其完成。由于 Future.__iter__ = Future.__await__(见 CPython 源码),Python 允许“伪迭代”未完成的 Future,但此时会触发运行时检查并抛出上述异常。
✅ 正确做法是:必须显式 await gather() 的返回值,再进行解包或构造字典。以下是两种推荐实现方式:
✅ 方式一:在字典推导式中 await 整个 gather 结果(推荐)
import asyncio
async def div7_tuple(x):
await asyncio.sleep(0.01) # 模拟异步 I/O
return x, x / 7
async def main():
lost = [4, 8, 15, 16, 23, 42]
# ✅ 正确:先 await gather 得到结果列表,再解包构建字典
results = await asyncio.gather(*[div7_tuple(x) for x in lost])
result_dict = {k: v for k, v in results}
print(result_dict)
# 输出示例:{4: 0.5714285714285714, 8: 1.1428571428571428, ...}
if __name__ == '__main__':
asyncio.run(main())✅ 方式二:先 await 再用 dict() 构造(更简洁)
async def main():
lost = [4, 8, 15, 16, 23, 42]
# ✅ 等价写法:gather 后直接转为 dict
pairs = await asyncio.gather(*[div7_tuple(x) for x in lost])
result_dict = dict(pairs)
print(result_dict)⚠️ 注意事项:
立即学习“Python免费学习笔记(深入)”;
- ❌ 错误写法:awaitables = asyncio.gather(...); {k: v for k, v in awaitables} —— awaitables 是 Future,不能直接用于推导式;
- ✅ 正确顺序永远是:await asyncio.gather(...) → 得到 list[tuple] → 再处理;
- 若需保持键值顺序(Python
- 避免在 gather() 内部混用同步与异步调用;所有被 gather 并发执行的目标必须是协程对象(即 awaitable)。
? 底层补充:Future.__await__() 方法定义了其可等待行为;当 Future.done() 为 False 时,__await__ 会 yield self 触发挂起;若用户跳过 await 直接迭代(如推导式中),则 __await__ 被隐式调用但未完成,最终抛出 RuntimeError。这是 Python 异步语义强约束的体现,而非 bug。
掌握这一原则后,你不仅能修复字典推导问题,也能举一反三地正确处理 asyncio.gather() 在列表、集合等其他推导式中的使用场景。










