asyncio.gather(return_exceptions=True)返回混合结果列表,元素为正常值或Exception实例,需用isinstance(item, Exception)逐项判断;成功结果与异常可分别用列表推导式提取,并注意异常序列化、日志及内存问题。

asyncio.gather(return_exceptions=True) 返回的是混合结果列表
调用 asyncio.gather(return_exceptions=True) 后,返回值是一个 list,其中每个元素要么是对应协程的正常返回值,要么是一个 Exception 实例(不是被抛出,而是作为值存在)。这意味着你不能直接对整个结果做逻辑判断或解包,必须逐项检查类型。
- 常见错误:把结果当纯成功值用,比如
result[0].some_method(),结果在第 0 项是TimeoutError时直接报AttributeError - 正确做法是统一用
isinstance(item, Exception)判断,而不是靠try/except包裹访问 - 注意:
BaseException子类(如SystemExit,KeyboardInterrupt)也可能出现,但通常return_exceptions=True只捕获协程内抛出的异常,不包括事件循环中断
如何安全提取成功结果并分类异常
最常用模式是用列表推导式分离成功值和异常,同时保留原始索引关系便于调试。不要用 filter 或 map 隐藏类型判断逻辑,可读性差且难调试。
-
[r for r in results if not isinstance(r, Exception)]—— 只取成功结果 -
[(i, r) for i, r in enumerate(results) if isinstance(r, Exception)]—— 带索引的异常列表,方便定位哪个任务失败 - 如果需要区分异常类型再处理,用
isinstance(r, TimeoutError)或type(r).__name__,避免用str(r)做模糊匹配 - 别忘了:空异常列表不等于“全部成功”,要确认
len(results) == len(expected_tasks),防止任务数传错导致漏判
与 return_exceptions=False 的行为差异直接影响错误传播
设为 False(默认)时,只要任一协程抛异常,gather 立即中止并把第一个异常原样抛出,其余协程可能还在运行(取决于是否已取消)。而 True 下所有协程都会完成,异常被“吞”进结果里——这是两种完全不同的错误处理契约。
- 选
True的典型场景:批量请求 API,允许部分失败,后续要聚合统计或重试 - 选
False的典型场景:多个强依赖步骤(如先 auth 再 fetch),一个失败整条链无意义 - 性能影响:两者调度开销几乎一致,但
True会多一次结果遍历判断,对几千个任务才需留意 - 兼容性:Python 3.7+ 行为一致,无需额外适配
常见陷阱:异常对象未被显式处理就进入日志或序列化
把整个 results 直接传给 logging.error(results) 或 json.dumps(results) 会失败——Exception 实例不可 JSON 序列化,logging 默认只打 repr,但可读性差。
立即学习“Python免费学习笔记(深入)”;
- 写日志前先格式化异常:
str(e)或traceback.format_exception(type(e), e, e.__traceback__) - 序列化前过滤或转换:
[r if not isinstance(r, Exception) else {'error': type(r).__name__, 'message': str(r)} for r in results] - 别忽略异常的
__traceback__属性——它指向协程内出错位置,但仅在异常被 raise 过才有效;return_exceptions=True下该属性仍保留,可用traceback.print_exception()打印完整上下文 - 最容易被忽略的一点:异常对象本身可能持有大内存引用(比如它捕获了某个大型 response 对象),不及时清理可能引发意外内存滞留










