exceptiongroup 是 python 3.11 引入的原生并发异常容器类型,本质是继承自 baseexception 的异常集合,含 exceptions 元组存储子异常,需用 except* 显式匹配处理,不可与普通 except 混用。

ExceptionGroup 是什么,和普通异常有什么本质区别?
ExceptionGroup 不是装饰性语法糖,而是 Python 3.11 引入的原生并发异常容器类型。它解决的核心问题是:当多个子任务(比如 asyncio.gather 或 concurrent.futures 中并行执行)各自抛出异常时,传统单异常机制只能暴露其中一个,其余被静默丢弃。
它和普通异常的关键差异在于:
-
ExceptionGroup是异常的“集合”,本身继承自BaseException,但不是Exception - 它包含一个
exceptions属性(tuple),按顺序保存所有子异常 - 它不自动展开子异常——你必须显式遍历或用
except*捕获,否则会被当作单个异常忽略掉子项
用 except* 精准匹配子异常类型(不是 except)
except* 是专为 ExceptionGroup 设计的捕获语法,和传统 except 互斥且不可混用。写成 except ValueError: 无法捕获 ExceptionGroup 中的 ValueError 子项。
正确做法是:
-
except* ValueError:—— 匹配组内所有ValueError子异常,匹配后该子异常从组中移除 - 若组内剩余未被任何
except*处理的异常,则整个ExceptionGroup会重新抛出 - 支持多个
except*分段处理,顺序重要:先写的先匹配
try:
raise ExceptionGroup("fetch failed", [
ValueError("timeout"),
ConnectionError("refused"),
ValueError("empty response")
])
except* ValueError as eg:
print(f"ValueErrors: {len(eg.exceptions)}") # → 2
except* ConnectionError:
print("network issue")
注意:不能在同一个 try 块里混用 except 和 except<em></em>,Python 会报 SyntaxError: cannot mix 'except' and 'except'。
asyncio.gather() 默认就可能抛 ExceptionGroup
asyncio.gather() 在 return_exceptions=False(默认)时,只要任一协程出错,就会把所有异常打包成 ExceptionGroup 抛出——这点和旧版行为完全不同(旧版只抛第一个)。
常见误操作:
- 直接写
except Exception:试图兜底 → 失败,因为ExceptionGroup不是Exception的子类 - 忘记加
*,写成except ValueError:→ 匹配不到,最终未处理的ExceptionGroup向上冒泡,导致崩溃
正确模式:
import asyncio
<p>async def fetch_user():
raise ValueError("user not found")</p><p>async def fetch_config():
raise KeyError("missing key")</p><p>async def main():
try:
await asyncio.gather(fetch_user(), fetch_config())
except<em> ValueError as eg:
print("user fetch failed:", eg.exceptions[0])
except</em> KeyError as eg:
print("config fetch failed:", eg.exceptions[0])</p>
如果只想让某个失败不影响整体,可提前设 return_exceptions=True,这时 gather 返回结果列表(含 Exception 实例),不再抛 ExceptionGroup。
手动构造 ExceptionGroup 的典型场景
你不需要总等库自动抛——在封装批量操作工具函数时,主动用 ExceptionGroup 聚合错误更清晰。
适用场景包括:
- 批量文件读取,部分文件权限拒绝、部分路径不存在
- 多 API 并发调用,统一收集各端点错误
- 自定义线程池执行器中汇总子线程异常
构造方式很简单:
exc_list = []
for path in paths:
try:
with open(path) as f:
process(f)
except PermissionError as e:
exc_list.append(e)
except FileNotFoundError as e:
exc_list.append(e)
<p>if exc_list:
raise ExceptionGroup("batch read failed", exc_list)</p>
注意:ExceptionGroup 的第一个参数是「总描述」,第二个是异常列表(不能是单个异常)。传空列表会抛 ValueError;传非异常对象会抛 TypeError。
ExceptionGroup 的嵌套是合法的,但调试时堆栈容易变深,除非明确需要分层归因,否则建议扁平化聚合。另外,它的 <strong>cause</strong> 和 <strong>context</strong> 行为与普通异常一致,但多数日志库尚未适配其结构,直接打印可能只显示顶层 group 信息。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











