Python拼接协程的运行结果,平铺成一个序列(KIMI)
本文为和KIMI的对话记录,仅供参考。
协程结果平铺成序列
User: Python中想要把异步函数返回的列表拼接起来可以这么写吗?
flags = await itertools.chain.from_iterable(request_once(session, text, batch) for batch in nodes_batched)
Kimi: 你这段代码 不能 按预期工作,原因有两个关键点:
❌ 问题 1:itertools.chain.from_iterable 是同步的,不能 await
itertools.chain.from_iterable 是一个同步工具,它不接受协程(coroutine),也不会帮你 await 异步函数。
你传进去的是一个生成器表达式:
(request_once(session, text, batch) for batch in nodes_batched)
这个生成器里每个元素都是一个 协程对象(coroutine),而不是已经拿到的列表。itertools.chain.from_iterable 不会等你 await 它们,它只会把协程对象当成普通对象处理,结果自然不是你想要的。
❌ 问题 2:await 的位置不对
你写的是:
flags = await itertools.chain.from_iterable(...)
但 itertools.chain.from_iterable(...) 返回的是一个迭代器,不是协程,不能 await。
✅ 正确写法:先 await 所有异步调用,再拼接
你应该先 await 每个异步调用,拿到它们返回的列表,然后再用 itertools.chain.from_iterable 拼接:
# 先并发执行所有异步调用
results = await asyncio.gather(
*(request_once(session, text, batch) for batch in nodes_batched)
)
# 再将所有列表拼成一个迭代器
flags = list(itertools.chain.from_iterable(results))
✅ 更简洁的写法(如果你不需要懒加载):
flags = [item for sublist in await asyncio.gather(
*(request_once(session, text, batch) for batch in nodes_batched)
) for item in sublist]
✅ 总结一句话:
先
await所有协程,拿到结果列表,再拼接。
如果你还有并发控制、限速等需求,可以考虑用 asyncio.Semaphore 或 asyncio.as_completed,我可以帮你写。
Async和Await简单用法
User: 能简单介绍一下async和await的用法吗?
Kimi: 一句话:
async 把函数 变成协程,await 把协程 真正挂起并等待结果——二者必须成对出现,缺一不可。
1. 最基本模板
import asyncio
async def say_after(delay, what): # 1. 声明协程
await asyncio.sleep(delay) # 2. 遇到 IO 挂起,让出 CPU
print(what)
async def main():
await say_after(1, 'hello') # 3. 等待协程完成
await say_after(2, 'world')
asyncio.run(main()) # 4. 入口:启动事件循环
输出(时间轴)
hello → 1 秒后
world → 再 2 秒后
2. 并发提速
async def main():
# 两个协程一起跑,总耗时 ≈ max(1,2) = 2 秒
t0 = asyncio.create_task(say_after(1, 'hello'))
t1 = asyncio.create_task(say_after(2, 'world'))
await t0
await t1
3. 收集批量结果
urls = [f'https://httpbin.org/delay/{i}' for i in range(5)]
async def fetch(session, url):
async with session.get(url) as r:
return await r.text()
async def main():
async with aiohttp.ClientSession() as s:
htmls = await asyncio.gather(*(fetch(s, u) for u in urls))
print('got', len(htmls), 'pages')
4. 常见误区
| 错误写法 | 原因 |
|---|---|
time.sleep(1) |
阻塞整个线程,事件循环卡住 |
requests.get(...) |
同步库,同理会卡循环 |
await 用在普通函数 |
普通函数不是协程,会抛 TypeError |
忘记 await |
协程对象没执行,得到的是 <coroutine object ...> |
5. 口诀
- 想异步 → 函数加
async - 想等待 → 前面加
await - 想并发 → 用
asyncio.create_task或asyncio.gather - 想启动 →
asyncio.run(main())一站式入口
记住这四步,90 % 的异步代码都能写出来。
浙公网安备 33010602011771号