<p>ExceptionGroup是BaseException的直接子类,非Exception子类,故except Exception:无法捕获;需用except ExceptionGroup:或except * ValueError:语法,且二者不可混用。</p>

ExceptionGroup不是普通异常的子类
Python 3.11 引入 ExceptionGroup 是为支持结构化并发(如 asyncio.gather、task_group),但它**没有继承自 BaseException 的单异常路径**——它本身是 BaseException 的直接子类,而常规 except Exception: 只捕获 Exception 及其子类,不包含 ExceptionGroup(因为后者是并列关系,不是 Exception 的后代)。
所以哪怕你抛出一个纯由 ValueError 组成的 ExceptionGroup,except ValueError: 或 except Exception: 都不会触发。
-
except ExceptionGroup:才能直接捕获(需显式导入:from exceptiongroup import ExceptionGroup,或 Python 3.11.5+ 原生支持) -
except * ValueError:是专用语法,用于“匹配组内任意一个子异常”,不是传统except - 裸
except:能捕获(因它捕获所有BaseException),但会同时吞掉SystemExit、KeyboardInterrupt等,不推荐
except * 语法不能嵌套或混用普通 except
except * 是全新控制流机制,和传统 except 不兼容:它不能出现在同一个 try 块里与普通 except 共存,也不能在 except * 分支里再写 except。
常见误写:except * ValueError: 后跟 except TypeError: ——这会直接报 SyntaxError: cannot mix 'except *' and 'except'。
立即学习“Python免费学习笔记(深入)”;
- 必须全用
except *处理ExceptionGroup,或全用传统方式(配合isinstance(e, ExceptionGroup)手动展开) -
except * ValueError as e:中的e是一个新ExceptionGroup(只含匹配的子异常),不是单个ValueError实例 - 若想降级处理(比如先尝试
except * ValueError,再 fallback 到通用逻辑),得用外层try/except包裹整个try/except *块
asyncio.gather 默认不提升 ExceptionGroup
很多人以为 asyncio.gather(..., return_exceptions=False)(默认)会像旧版一样抛出第一个异常,但实际上 Python 3.11+ 中,只要有一个子任务失败,它就直接抛 ExceptionGroup,哪怕只有一个失败任务。
这意味着:升级到 3.11 后,原本能被 except ValueError: 捕获的代码,现在会静默漏掉异常,变成未处理的 ExceptionGroup。
- 临时兼容方案:
return_exceptions=True,然后手动遍历结果列表,用isinstance(x, BaseException)检查每个元素 - 更稳妥的做法:始终用
try/except * ...或显式检查isinstance(e, ExceptionGroup)并调用e.exceptions展开 - 注意
asyncio.create_task单独启动的任务不会自动聚合;只有gather、TaskGroup这类明确支持结构化并发的 API 才会生成ExceptionGroup
调试时容易忽略的 repr 和 traceback 行为
ExceptionGroup 的 __repr__ 和 traceback 输出格式和传统异常不同:它会展开子异常树,但默认不显示完整嵌套路径,IDE 或日志工具可能截断或错误解析。
例如,raise ExceptionGroup("db", [ValueError("timeout"), TypeError("null")]) 的 traceback 会分两块显示两个异常,但如果你用 logging.exception(),可能只打出第一段。
- 打印完整结构用:
import traceback; traceback.print_exception(e)(Python 3.11.5+ 支持原生打印ExceptionGroup) -
str(e)只返回顶层消息(如"db"),不包含子异常内容;要获取全部信息,必须遍历e.exceptions - 某些测试框架(如 pytest)对
ExceptionGroup的断言支持尚不完善,pytest.raises(ValueError)无法匹配组内ValueError,得改用pytest.raises(ExceptionGroup)+ 手动检查e.exceptions
最常被忽略的一点:ExceptionGroup 的存在不是为了替代单异常,而是为了保留并发失败的「上下文完整性」。试图用传统 try/except “绕过”它,往往意味着你在丢弃关键的错误来源信息。


















