
Python 的异常链机制不是简单的“抛出新异常”,而是通过 __cause__ 和 __context__ 两个隐式属性,把多个异常按逻辑关系串联起来,让错误溯源更清晰。
异常链的两种触发方式
Python 提供两种明确建立异常链的语法,对应不同语义:
-
raise new_exc from old_exc:显式设置new_exc.__cause__ = old_exc,表示“新异常是因旧异常直接导致的”。这是因果链,调试时会显示The above exception was the direct cause of the following exception。 -
raise new_exc(在捕获异常的except块中):自动设置new_exc.__context__ = old_exc,表示“新异常是在处理旧异常时发生的”,属于伴随上下文。默认不抑制旧异常回溯,除非加from None。
__cause__ 与 __context__ 的区别
两者都存前序异常对象,但用途和显示行为不同:
-
__cause__是手动指定的因果关系,优先级高;一旦设置,__context__就不会在 traceback 中自动显示。 -
__context__是 Python 自动记录的“上一个未被处理的异常”,仅在无__cause__且未用from None抑制时才参与 traceback 渲染。 - 若想彻底断开链(比如封装异常后不想暴露底层细节),写
raise NewError() from None,此时__cause__为None,__context__虽存在但被忽略。
异常链如何影响 traceback 输出
Python 解释器在打印异常时,会按规则展开链:
立即学习“Python免费学习笔记(深入)”;
- 先完整打印最内层异常(即最后抛出的那个)。
- 如果它有
__cause__,就接着打印__cause__异常,并标注“direct cause”。 - 如果没有
__cause__,但有非空__context__且未被抑制,就打印__context__异常,并标注“during handling of…”。 - 这个过程可递归,形成多层嵌套 traceback,但只展示显式关联或自动捕获的那一条主线。
实际使用建议
异常链不是炫技工具,关键在提升可维护性:
- 封装底层库异常时,用
raise MyError("timeout") from e,保留原始超时细节。 - 做兜底处理(如日志+重抛)时,避免意外激活
__context__,必要时加from None控制信息粒度。 - 自定义异常类无需额外操作——只要用标准
raise ... from ...,链就自动建立;检查链用exc.__cause__或traceback.print_exception()即可。










