exceptiongroup是python 3.11引入的结构化异常容器,用于并发场景中聚合多个独立异常;它继承自baseexception而非exception,需用except exceptiongroup:或except*语法捕获,支持嵌套且默认不被except exception:捕获。

ExceptionGroup 是什么,和普通异常有什么区别?
ExceptionGroup 是 Python 3.11 引入的原生类型,用于在并发场景(比如 asyncio.gather() 或 concurrent.futures)中一次性抛出多个独立异常。它不是“多个异常的列表”,而是一个结构化异常容器:顶层是 ExceptionGroup 实例,其 exceptions 属性才是元组形式的子异常。
关键区别在于:
- 普通
except Exception:不会捕获ExceptionGroup(它继承自BaseException,但不是Exception的子类) - 必须显式用
except ExceptionGroup:或更宽泛的except BaseException:才能接住 -
ExceptionGroup支持嵌套,子异常可以是普通异常,也可以是另一个ExceptionGroup
用 asyncio.gather() 触发并捕获 ExceptionGroup
asyncio.gather() 在 return_exceptions=False(默认)时,只要任一协程出错,就立刻以 ExceptionGroup 包裹所有已完成的异常抛出(注意:未完成的协程会被取消)。
常见错误现象:
- 直接写
except Exception:→ 完全捕获不到,程序崩溃并显示ExceptionGroup: unhandled exception - 忘记检查
eg.exceptions→ 以为只有一种错误,实际可能混着TimeoutError、ConnectionError、ValueError
实操建议:
- 使用
except ExceptionGroup as eg:显式捕获 - 遍历
eg.exceptions分别处理,或用except* ValueError:(Python 3.11+ 的新语法)匹配子异常类型 - 示例:
import asyncio
async def fail_fast(): raise ValueError("bad input")
async def timeout_later(): await asyncio.sleep(0.1) raise TimeoutError("slow response")
try: await asyncio.gather(fail_fast(), timeout_later()) except ExceptionGroup as eg: for e in eg.exceptions: print(f"sub-error: {type(e).name} – {e}")
except* 语法怎么用,和传统 except 有何不同?
except* 是 Python 3.11 专为 ExceptionGroup 设计的匹配语法,它会自动遍历 eg.exceptions,只捕获其中匹配的子异常,未匹配的子异常会自动重新组合成新的 ExceptionGroup 继续向上抛。
使用场景:
图片提示词生成器?不止如此。 马甲系统 —— 把脑海中的画面,翻译成AI能理解的专业表达。 用得越多,它越懂你:首次需要多问几句确认方向,用久了几乎一说就懂。 用得越多,它越快:缓存机制让后续对话越来越省。 RAG进化:成功案例持续入库,越跑越聪明。 输入「新手指南」查看完整功能介绍
- 你想处理部分已知错误(如网络类),但保留其他未知错误让外层统一兜底
- 多个任务可能抛不同异常,你只想 log 其中一种,其余仍需中断流程
容易踩的坑:
-
except* Exception:看似兜底,但其实只匹配子异常中的Exception类型,不包括SystemExit、KeyboardInterrupt等BaseException子类 - 一个
except*块里不能同时捕获同一子异常两次(语法报错) - 如果所有子异常都被某个
except<em></em>捕获,就不会再触发后续的except或except
示例:
try:
await asyncio.gather(fail_fast(), timeout_later())
except* ValueError as eg:
print("got value errors:", len(eg.exceptions))
except* TimeoutError:
print("timeout handled separately")
except ExceptionGroup as eg:
print("other errors remain:", [type(e).__name__ for e in eg.exceptions])
并发任务失败后,如何避免丢失原始 traceback?
ExceptionGroup 默认的 <strong>str</strong> 和 print() 输出较简略,只显示异常类型和消息,不自动展开每个子异常的完整 traceback。调试时容易误判哪条路径真正出错。
性能与兼容性影响:
-
traceback.print_exception()对ExceptionGroup有专门支持(Python 3.11+),但旧版工具(如某些日志库)可能直接调用str(eg)导致信息丢失 - 不要手动拼接
repr(e)—— 会丢掉上下文和行号
实操建议:
- 日志输出时用
traceback.print_exception(eg)(注意传入的是整个ExceptionGroup,不是eg.exceptions) - 若需结构化记录,可遍历
eg.exceptions并对每个调用traceback.format_exception() - 第三方库如
loguru3.11+ 版本已适配,但logging模块原生不格式化ExceptionGroup,需自行封装
最简可靠方式:
import traceback <p>try: ... except ExceptionGroup as eg: traceback.print_exception(eg) # ← 这行才真正打印全部 traceback</p>
嵌套 ExceptionGroup 和跨线程传播仍是易忽略点——比如用 concurrent.futures.ThreadPoolExecutor 调用异步函数,错误可能被二次包装,需要多层 except* 或递归展开 exceptions。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!










