直接在asyncio协程中用tqdm会卡住,因其默认阻塞刷新且非awaitable;应使用tqdm.asyncio.tqdm配合async for,或手动调用pbar.refresh()并穿插await asyncio.sleep(0)。

为什么直接在 asyncio 协程里用 tqdm 会卡住或不更新?
因为 tqdm 默认依赖 sys.stdout 的阻塞式刷新,而 asyncio 的事件循环通常运行在单线程中,且协程可能长时间不交出控制权(比如没加 await asyncio.sleep(0)),导致进度条无法及时重绘。更关键的是,tqdm 内部的刷新逻辑不是 awaitable,不能直接混入 async def 函数体中同步调用。
用 tqdm.asyncio.tqdm 替代原生 tqdm 是最简方案
新版 tqdm(≥4.64.0)内置了对 asyncio 的支持,提供专用异步迭代器包装器。它会在每次 await 迭代时自动刷新,并兼容 async for 语法。
- 确保安装最新版:
pip install -U tqdm - 必须用
async for遍历,不能用普通for - 底层仍依赖
asyncio.sleep(0)让出控制权,所以被包装的异步可迭代对象(如AsyncIterator)需真正是异步的
import asyncio
from tqdm.asyncio import tqdm
<p>async def fetch_item(i):
await asyncio.sleep(0.1) # 模拟异步 IO
return f"item-{i}"</p><p>async def main():
tasks = [fetch_item(i) for i in range(10)]</p><h1>✅ 正确:用 tqdm.as_completed 包装 asyncio.gather 的结果流</h1><pre class="brush:php;toolbar:false;">for result in tqdm.asyncio.as_completed(tasks):
await result # 注意:as_completed 返回的是 Future,需 await或更常见场景:遍历一个 async generator
async def data_stream(): for i in range(5): await asyncio.sleep(0.2) yield i
async def process_stream(): async for i in tqdm.asyncio.tqdm(data_stream(), total=5): await asyncio.sleep(0.05) # 模拟处理
手动集成时如何避免「进度条不动」和「输出错乱」?
若你无法用 tqdm.asyncio(比如要自定义更新逻辑、或配合非迭代型任务),就得手动控制刷新节奏。核心原则是:每次更新后显式调用 pbar.refresh(),并确保刷新发生在事件循环能调度的时机。
仅使用 Python 将 Claw Code 虚拟化框架与 OpenClaw 集成,提供 184 种工具和 200 多条命令,用于安全分析和代码质量...
- 不要在纯 CPU 密集循环里调用
pbar.update(),应穿插await asyncio.sleep(0) - 使用
position和leave参数避免多进度条覆盖(尤其嵌套协程) - 关闭
dynamic_ncols=True可减少终端宽度变化引发的错位 - 若在 Jupyter 中使用,需传
file=sys.stdout并确保内核支持实时 flush
import asyncio import sys from tqdm import tqdm <p>async def manual_progress(): pbar = tqdm(total=100, file=sys.stdout, dynamic_ncols=False) for i in range(100): await asyncio.sleep(0.01) pbar.update(1) pbar.refresh() # 关键:强制刷新,否则可能积压 pbar.close() </p>
遇到 RuntimeWarning: coroutine 'tqdm.__iter__' was never awaited 怎么办?
这是最常见的误用:把 tqdm(async_iter) 当作同步迭代器用了,但传入的是 async generator,而你写了 for x in tqdm(...) —— 这会触发 __iter__,但该方法返回的是 coroutine 对象,不是 iterator。
- ✅ 正确写法只有两种:
async for x in tqdm.asyncio.tqdm(...)或for x in tqdm(sync_list) - ❌ 错误写法:
for x in tqdm(async_gen)、list(tqdm(async_gen)) - 如果必须从 async generator 构建列表,先用
await asyncio.gather(*[a for a in async_gen])收集,再用同步tqdm
实际项目里最容易被忽略的是:tqdm 的 total 参数在异步流中往往无法预知,此时要么估算(影响精度),要么改用 tqdm(..., unit="it", unit_scale=True) 显示速率而非绝对进度。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!










