马儿慢些走

人生惶惶,悲喜一场。

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.Semaphoreasyncio.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_taskasyncio.gather
  • 想启动 → asyncio.run(main()) 一站式入口

记住这四步,90 % 的异步代码都能写出来。

posted on 2025-09-18 20:02  马儿慢些走  阅读(11)  评论(0)    收藏  举报

导航