except*是Python 3.11专为解包ExceptionGroup设计的语法,它匹配异常组内所有指定类型的子异常并返回新ExceptionGroup,而普通except仅捕获单个异常,二者不可混用且语义完全不同。

什么是 except*,它和普通 except 有什么本质区别
except* 是 Python 3.11 引入的专门用于捕获 ExceptionGroup(异常组)的语法,不是用来替代传统单异常处理的。当你用 asyncio.gather(..., return_exceptions=False) 或显式构造 ExceptionGroup 时,可能一次性抛出多个不相关的异常——这时普通 except ValueError: 根本匹配不上,会直接崩溃。
关键点在于:except* 不是“捕获任意一个异常”,而是“从异常组中筛选出匹配类型的子集,并只对这些子集执行对应块”,其余未被匹配的异常仍保留在组里,供后续 except* 块继续处理或最终冒泡。
常见错误现象:SyntaxError: invalid syntax(如果在 Python except*)、或运行时报 Uncaught ExceptionGroup(漏写了匹配块,或类型写错)。
怎么写一个能触发 except* 的最小可运行例子
必须先制造一个 ExceptionGroup。最直接方式是用 raise ExceptionGroup("msg", [exc1, exc2]),或者用 asyncio.gather 并让多个协程分别抛异常(且不设 return_exceptions=True)。
立即学习“Python免费学习笔记(深入)”;
示例:
仅使用 Python 将 Claw Code 虚拟化框架与 OpenClaw 集成,提供 184 种工具和 200 多条命令,用于安全分析和代码质量...
try:
raise ExceptionGroup("eg", [ValueError("bad int"), TypeError("wrong type")])
except* ValueError as eg:
print(f"捕获到 {len(eg.exceptions)} 个 ValueError")
except* TypeError as eg:
print(f"捕获到 {len(eg.exceptions)} 个 TypeError")
注意:eg 在 except* 中绑定的是一个新类型的 BaseExceptionGroup 实例(不是原始列表),其 .exceptions 属性才是匹配上的异常列表。
使用场景:批量任务失败后分类处理——比如网络请求中部分超时、部分认证失败、部分 JSON 解析错误,你想分别记录日志、重试或丢弃。
except* 的匹配逻辑和容易踩的坑
匹配是“子类型检查 + 组内筛选”,不是全量遍历。Python 会扫描异常组中每个异常,把类型匹配(或其子类)的挑出来组成新的 ExceptionGroup 传入对应块。没被任何 except* 捕获的异常,最终会以未处理的 ExceptionGroup 再次抛出。
- 不能混用
except和except*在同一个try中——会报SyntaxError -
except*块里不能再用raise抛出原eg,否则可能重复触发;应抛eg.exceptions[0]或新建ExceptionGroup - 如果异常组里有嵌套的
ExceptionGroup(即多层嵌套),except*默认只扁平化一层;需要手动递归展开才能处理深层异常 -
except* Exception能捕获所有,但会吞掉本该由后续块处理的异常——顺序很重要,宽泛类型要放后面
和 asyncio.gather 配合使用的实际注意事项
这是 except* 最典型的落地场景。默认情况下,asyncio.gather(a(), b(), c()) 只要有一个协程抛异常,整个调用就失败,且返回的是 ExceptionGroup(Python 3.11+)。
性能影响很小,但要注意:
- 必须确保事件循环运行在 Python 3.11+,且协程确实抛出了不同类型的异常(否则看起来就像普通异常)
- 不要设
return_exceptions=True,否则得到的是包含异常对象的列表,不是ExceptionGroup,except*就失效了 - 调试时打印
repr(exc)很重要——ExceptionGroup的字符串表示会明确标出嵌套结构,帮你确认是否真进了except* - 若想忽略某类异常(如
CancelledError),得显式写except* CancelledError: pass,否则它会留在组里导致最终报错
嵌套异常组的展开逻辑容易被忽略:如果你的某个子任务自己又抛了 ExceptionGroup,外层 except* 不会自动穿透进去,得靠 eg.subgroup() 或手动遍历 eg.exceptions 处理。

















