exceptiongroup是python 3.11引入的继承自baseexception的不可变异常容器,专为结构化捕获并行任务中的多个异常而设计,支持保留各子异常原始traceback及except*精确匹配。

ExceptionGroup 是 Python 3.11 引入的原生机制,专为捕获并行执行中产生的多个异常而设计——它不是“多个 except 的语法糖”,而是结构化错误流的第一等公民。如果你在 asyncio.gather(..., return_exceptions=False) 或 concurrent.futures.Executor.map 中遇到“只看到第一个异常”“堆栈被吞掉”“无法区分哪个子任务失败”,那 ExceptionGroup 就是你要的答案。
ExceptionGroup 是什么,和普通元组/列表有什么本质区别
它是一个不可变的异常容器,继承自 BaseException,因此能被 except 捕获;同时自带 exceptions 属性(元组),支持递归展开。关键点在于:它能保留每个子异常的原始 traceback,且能被 except* 精确匹配。
常见误用是把它当 list[Exception] 处理:
- ❌
raise [ValueError("a"), TypeError("b")]—— 类型错误,根本 raise 不出去 - ❌
except (ValueError, TypeError):—— 匹配不到ExceptionGroup,因为它是独立类型 - ✅ 正确入口:用
ExceptionGroup("msg", [exc1, exc2])构造,或依赖asyncio.gather/task_group自动产生
用 except* 精确提取特定异常类型
except* 是 Python 3.11 新增的异常捕获语法,专为拆解 ExceptionGroup 设计。它不是“捕获一个异常”,而是“从异常树中筛选出匹配的子异常,并返回新的 ExceptionGroup”。
SkillSub Pro - Python 题解与代码注释双功能技能功能概述SkillSub Pro - Python 题解与代码注释双功能技能是一项面向实际任务的技能,主要用于SkillSub Pro 是一个 Python 题解生成与代码注释的 双功能合体技能 ,专为学生、算法学习者和开发者设计;✅ 一个技能,两种用途 :;核心要点📝 题解模式 :输入题目/题号,自动生成完整 Python 题解(含详细注释、解题思路、复杂度分析);💬 注释模式 :输入 Python 代码,自动添加详细中。它将相关步骤、
示例场景:并发请求 3 个 API,其中 2 个超时、1 个返回 404,你想分别处理:
try:
results = await asyncio.gather(
fetch("https://api.a"),
fetch("https://api.b"),
fetch("https://api.c"),
return_exceptions=False
)
except* asyncio.TimeoutError as eg:
print(f"超时了 {len(eg.exceptions)} 次")
for e in eg.exceptions:
print("→", e)
except* HTTPStatusError as eg:
print(f"HTTP 错误 {len(eg.exceptions)} 次")
# 注意:eg 本身仍是 ExceptionGroup,可继续 .exceptions 遍历
-
except*块内,eg是子集ExceptionGroup,不是单个异常 - 一个
ExceptionGroup可同时触发多个except*分支(比如既有 TimeoutError 又有 ConnectionError) - 没被任何
except*匹配的异常,会继续向上抛出 —— 所以顺序重要,宽泛类型(如Exception)建议放最后
手动构造 ExceptionGroup 的典型用途与陷阱
多数情况你不需要手动构造,但以下场景值得主动用:批量校验参数后聚合报错、测试中模拟多错误路径、封装旧代码使其兼容新异常模型。
示例:校验一组用户输入,不中断收集所有错误:
def validate_users(users):
errors = []
for u in users:
if not u.email:
errors.append(ValueError(f"user {u.id}: email missing"))
if len(u.name)
- ⚠️ 错误:传入空列表
[]会引发ValueError("empty exceptions")——ExceptionGroup不允许空构造 - ⚠️ 错误:混用不同异常类型却只用一个
except*捕获,可能漏掉语义差异(比如ValueError和TypeError应该分开处理) - ✅ 推荐:给
ExceptionGroup的message参数起有意义的名字(如"user_validation"),方便日志分类
真正难的是异常上下文的跨层级透传:比如你在 except* 里拿到一个子 ExceptionGroup,又想把它作为另一个 ExceptionGroup 的成员重新抛出 —— 这时得显式调用 add_note() 或嵌套构造,否则原始 traceback 会丢失。别指望它自动“扁平化”。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!










