ExceptionGroup是Python 3.11引入的结构化异常容器,用于并发场景中聚合多个独立异常;它继承自BaseException而非Exception,需用except ExceptionGroup:或except*语法捕获,支持嵌套且默认不被except Exception:捕获。

ExceptionGroup 是什么,和普通异常有什么区别?
ExceptionGroup 是 Python 3.11 引入的原生类型,用于在并发场景(比如 asyncio.gather() 或 concurrent.futures)中一次性抛出多个独立异常。它不是“多个异常的列表”,而是一个结构化异常容器:顶层是 ExceptionGroup 实例,其 exceptions 属性才是元组形式的子异常。
关键区别在于:
- 普通
except Exception:不会捕获ExceptionGroup(它继承自BaseException,但不是Exception的子类) - 必须显式用
except ExceptionGroup:或更宽泛的except BaseException:才能接住 -
ExceptionGroup支持嵌套,子异常可以是普通异常,也可以是另一个ExceptionGroup
用 asyncio.gather() 触发并捕获 ExceptionGroup
asyncio.gather() 在 return_exceptions=False(默认)时,只要任一协程出错,就立刻以 ExceptionGroup 包裹所有已完成的异常抛出(注意:未完成的协程会被取消)。
常见错误现象:
立即学习“Python免费学习笔记(深入)”;
- 直接写
except Exception:→ 完全捕获不到,程序崩溃并显示ExceptionGroup: unhandled exception - 忘记检查
eg.exceptions→ 以为只有一种错误,实际可能混着TimeoutError、ConnectionError、ValueError
实操建议:
- 使用
except ExceptionGroup as eg:显式捕获 - 遍历
eg.exceptions分别处理,或用except* ValueError:(Python 3.11+ 的新语法)匹配子异常类型 - 示例:
import asyncio </li></ul><p>async def fail_fast(): raise ValueError("bad input")</p><p>async def timeout_later(): await asyncio.sleep(0.1) raise TimeoutError("slow response")</p><p>try: await asyncio.gather(fail_fast(), timeout_later()) except ExceptionGroup as eg: for e in eg.exceptions: print(f"sub-error: {type(e).<strong>name</strong>} – {e}")</p>except* 语法怎么用,和传统 except 有何不同?
except*是 Python 3.11 专为ExceptionGroup设计的匹配语法,它会自动遍历eg.exceptions,只捕获其中匹配的子异常,未匹配的子异常会自动重新组合成新的ExceptionGroup继续向上抛。使用场景:
提示词大师-python版下载图片提示词生成器?不止如此。 马甲系统 —— 把脑海中的画面,翻译成AI能理解的专业表达。 用得越多,它越懂你:首次需要多问几句确认方向,用久了几乎一说就懂。 用得越多,它越快:缓存机制让后续对话越来越省。 RAG进化:成功案例持续入库,越跑越聪明。 输入「新手指南」查看完整功能介绍
- 你想处理部分已知错误(如网络类),但保留其他未知错误让外层统一兜底
- 多个任务可能抛不同异常,你只想 log 其中一种,其余仍需中断流程
容易踩的坑:
-
except* Exception:看似兜底,但其实只匹配子异常中的Exception类型,不包括SystemExit、KeyboardInterrupt等BaseException子类 - 一个
except*块里不能同时捕获同一子异常两次(语法报错) - 如果所有子异常都被某个
except<em></em>捕获,就不会再触发后续的except或except
示例:
try: await asyncio.gather(fail_fast(), timeout_later()) except* ValueError as eg: print("got value errors:", len(eg.exceptions)) except* TimeoutError: print("timeout handled separately") except ExceptionGroup as eg: print("other errors remain:", [type(e).__name__ for e in eg.exceptions])并发任务失败后,如何避免丢失原始 traceback?
ExceptionGroup默认的<strong>str</strong>和print()输出较简略,只显示异常类型和消息,不自动展开每个子异常的完整 traceback。调试时容易误判哪条路径真正出错。性能与兼容性影响:
-
traceback.print_exception()对ExceptionGroup有专门支持(Python 3.11+),但旧版工具(如某些日志库)可能直接调用str(eg)导致信息丢失 - 不要手动拼接
repr(e)—— 会丢掉上下文和行号
实操建议:
- 日志输出时用
traceback.print_exception(eg)(注意传入的是整个ExceptionGroup,不是eg.exceptions) - 若需结构化记录,可遍历
eg.exceptions并对每个调用traceback.format_exception() - 第三方库如
loguru3.11+ 版本已适配,但logging模块原生不格式化ExceptionGroup,需自行封装
最简可靠方式:
import traceback <p>try: ... except ExceptionGroup as eg: traceback.print_exception(eg) # ← 这行才真正打印全部 traceback</p>
嵌套
ExceptionGroup和跨线程传播仍是易忽略点——比如用concurrent.futures.ThreadPoolExecutor调用异步函数,错误可能被二次包装,需要多层except*或递归展开exceptions。

















