exceptiongroup 和 baseexceptiongroup 是 python 3.11 引入的异常容器类型:前者继承 exception,用于普通异常分组;后者继承 baseexception,可包含 keyboardinterrupt 等系统级异常;二者均支持嵌套、遍历与 except* 精确匹配。

什么是 ExceptionGroup 和 BaseExceptionGroup?
Python 3.11 引入了结构化异常处理机制,核心是 ExceptionGroup(用于普通异常)和 BaseExceptionGroup(可包含 KeyboardInterrupt、SystemExit 等非 Exception 子类)。它们不是简单地把多个异常拼成字符串,而是真正可嵌套、可遍历、可匹配的异常容器。
-
ExceptionGroup继承自Exception,因此能被常规except Exception:捕获(但不推荐——会丢失分组信息) -
BaseExceptionGroup继承自BaseException,更底层,适用于asyncio或多线程中可能混入系统级中断的场景 - 两者都支持
exceptions属性(元组)和args(第一个参数为消息,第二个为异常列表)
怎么用 except* 捕获并分拣多种异常?
except* 是专为异常组设计的“星号捕获”语法,它不求全匹配,而是从异常组中提取所有匹配该类型的子异常,组成新的 ExceptionGroup 供你处理。
常见错误现象:写成 except ValueError: 试图捕获 ExceptionGroup 中的 ValueError,结果完全进不去分支——因为整个组本身是 ExceptionGroup 类型,不是 ValueError。
使用场景:
- 批量调用 API,部分失败返回
HTTPError,部分超时抛TimeoutError - 并行解析多个 JSON 文件,有的格式错(
JSONDecodeError),有的读取失败(FileNotFoundError)
实操建议:
- 每个
except*分支只处理一种预期异常类型,避免逻辑混杂 - 可连续写多个
except*,它们互不干扰,各自提取匹配项 - 如果某类异常一个都没匹配上,对应分支不会执行;剩余未被任何
except*捕获的异常,会继续向上抛出
try:
raise ExceptionGroup("batch failed", [
ValueError("item 1 invalid"),
TimeoutError("item 2 timeout"),
ValueError("item 3 invalid")
])
except* ValueError as eg:
print(f"Got {len(eg.exceptions)} value errors")
# eg.exceptions → (ValueError("item 1 invalid"), ValueError("item 3 invalid"))
except* TimeoutError as eg:
print("Timeout occurred")
怎么手动构造和抛出 ExceptionGroup?
别依赖自动聚合——目前只有 asyncio.gather(..., return_exceptions=False)(默认)或 task_group 在 trio 中会自动抛 ExceptionGroup。大多数批量任务得自己组装。
华为云 Python SDK 查询工具。自动发现已安装 SDK 的服务包、Client类、API 方法、入参详情、类信息(__init__ 参数、属性、类型、描述),支持关键字搜索,快速定位目标 API 使用方法。
容易踩的坑:
- 直接
raise ExceptionGroup("msg", [e1, e2])但其中某个e1是None或非异常实例 → 报TypeError - 混用
Exception和BaseException子类(如同时塞KeyboardInterrupt和ValueError)→ 必须用BaseExceptionGroup,否则报错
实操建议:
- 先收集所有异常(例如用
try/except包裹每个子任务,append 到列表) - 过滤掉
None,确认每个元素都是异常实例(isinstance(e, BaseException)) - 根据是否含系统级异常,决定用
ExceptionGroup还是BaseExceptionGroup
errors = []
for item in items:
try:
process(item)
except ValueError as e:
errors.append(e)
except OSError as e:
errors.append(e)
<p>if errors:
raise ExceptionGroup("processing failed", errors)</p>
为什么 except* 不能替代传统 except?
except* 只对 ExceptionGroup / BaseExceptionGroup 生效;遇到单个普通异常(比如单独一个 KeyError),它完全不触发。这是设计使然,不是 bug。
性能影响很小——except* 的匹配逻辑是遍历 exceptions 元组做 isinstance 判断,开销与异常数量线性相关,远小于实际出错带来的代价。
兼容性注意点:
- Python except* 语法,会直接 SyntaxError
- 即使在 3.11+,若上游库没升级,它抛的仍是普通异常或自定义聚合异常(非标准
ExceptionGroup),except*也捕获不到
真正容易被忽略的是:异常组里的异常对象本身不会被自动打印完整 traceback——默认 repr 只显示类型和消息。调试时得显式遍历 eg.exceptions 并用 traceback.print_exception() 才能看到每个子异常的上下文。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!










