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

什么是 except*?它和普通 except 有什么本质区别
except* 是 Python 3.11 引入的语法,专为处理 ExceptionGroup(异常组)设计,不是用来替代传统单异常捕获的。如果你用它捕获一个普通异常(比如 ValueError),会直接报 SyntaxError: invalid syntax —— 因为语法本身要求右侧必须是能匹配异常组中「部分子异常」的类型。
它的核心行为是:**尝试对异常组里的每个子异常分别匹配,成功则执行对应块,并从原组中移除该子异常;所有匹配失败后,剩余未处理的子异常会自动重新抛出为新的 ExceptionGroup**。
常见错误现象:
- 写 except* ValueError: 却捕获了一个纯 ValueError(非 ExceptionGroup),直接语法错误
- 在非 try 块里用 except*,或嵌套在普通 except 下,同样报错
怎么触发 ExceptionGroup 才能让 except* 生效
你不能手动写 raise ExceptionGroup(...) 来测试(虽然可以),但真实场景几乎都来自 asyncio.gather(..., return_exceptions=True) 或 threading / concurrent.futures 的批量任务失败,或者显式调用 raise ExceptionGroup("msg", [exc1, exc2])。
示例场景:
立即学习“Python免费学习笔记(深入)”;
import asyncio
<p>async def fails_twice():
raise ValueError("first")</p><p>async def fails_also():
raise TypeError("second")</p><p>async def main():</p><div class="aritcle_card flexRow">
<div class="artcardd flexRow">
<a class="aritcle_card_img" href="/xiazai/skill7154" title="python-pro"><img
src="https://img.php.cn/upload/skill/000/000/081/179134208595348.jpg" alt="python-pro" onerror="this.onerror='';this.src='/static/lhimages/moren/morentu.png'" ></a>
<div class="aritcle_card_info flexColumn">
<a href="/xiazai/skill7154" title="python-pro">python-pro</a>
<p>高级 Python 特性、异步编程、性能调优、静态类型、内存管理、Python 内部机制及生态库方面的专家。</p>
</div>
<a href="/xiazai/skill7154" title="python-pro" class="aritcle_card_btn flexRow flexcenter"><b></b><span>下载</span> </a>
</div>
</div><h1>这里会返回一个 ExceptionGroup,包含两个子异常</h1><pre class="brush:php;toolbar:false;">results = await asyncio.gather(
fails_twice(), fails_also(),
return_exceptions=True
)
if any(isinstance(r, BaseException) for r in results):
raise ExceptionGroup("batch failed", [r for r in results if isinstance(r, BaseException)])try: asyncio.run(main()) except ValueError as eg: print(f"Caught ValueError group: {eg.exceptions}") except TypeError as eg: print(f"Caught TypeError group: {eg.exceptions}")
注意:return_exceptions=True 是关键,否则 gather 遇到第一个异常就停了,不会形成异常组。
except* 的匹配逻辑和常见陷阱
它不是「只要有一个子异常匹配就进块」,而是「把所有匹配该类型的子异常打包进 eg,eg.exceptions 是 tuple,不是原始列表」。匹配后,这些子异常就从原组里剔除了,后续 except* 只看到剩下的。
- 多个
except*块按顺序执行,各自独立匹配当前剩余的异常组 -
except* Exception不会捕获所有 —— 它只捕获未被前面except*处理的、类型为Exception的子异常(不包括BaseException子类如SystemExit) - 不能混用
except和except*在同一个try中,否则SyntaxError -
except*块内再抛异常,会和剩余未处理子异常一起组成新ExceptionGroup向上传递
性能与兼容性要注意什么
ExceptionGroup 和 except* 是纯语言层新增机制,运行时开销极小,但调试体验目前较差:大多数 IDE 和 traceback 工具还没完全适配嵌套异常组的展开显示,print(eg) 输出可能只显示顶层信息。
兼容性上,Python except* 语法,连 import 都会失败。如果要写兼容代码,必须用字符串 exec 或版本检查绕过,但不推荐 —— 这类逻辑本身就表示你已在用 3.11+ 特性,应明确指定最低版本。
真正容易被忽略的是:**except* 永远不会“吞掉”整个异常组,除非你显式处理了全部子异常,否则总会有残留向上冒泡**。别指望它像普通 except 那样“兜底”,它的设计哲学是“精准分流,留白即告警”。

















