← [ BACK_TO_NOTES ]

Python 异常层次与异常链

内置异常层次、领域异常设计,以及 raise from / __cause__ 保留失败因果链。

#concept #growing #dev #python #python

[!info] related notes

Python 异常层次与异常链

这个概念为什么出现

线上只看到 ValueError: bad 却不知道是 JSON 解析失败还是业务校验失败,排障成本陡增。需要:

  • 合理的异常类型层次
  • raise New from original 保留因果

[!abstract] 一句话理解 异常是类层次;捕获应按具体程度排列;raise ... from ... 建立 __cause__ 链,便于日志与 except 策略。

最小可运行示例

先把示例放进业务场景,再看代码:

场景:支付回调解析失败包装为领域错误

# payment_exc_chain.py
# 业务意图:底层 JSON 错误包装为 PaymentCallbackError,保留 cause。
# 教学点:
# - 自定义异常;
# - raise from;
# - __cause__。

import json


class PaymentCallbackError(Exception):
    """支付回调无法被接受。"""


def parse_callback(raw: str) -> dict:
    try:
        data = json.loads(raw)
    except json.JSONDecodeError as exc:
        raise PaymentCallbackError("invalid callback json") from exc
    if not isinstance(data, dict):
        raise PaymentCallbackError("callback must be a JSON object")
    payment_id = data.get("payment_id")
    if not isinstance(payment_id, str) or not payment_id.strip():
        raise PaymentCallbackError("payment_id must be a nonempty string")
    return data


def main() -> None:
    try:
        parse_callback("{")
    except PaymentCallbackError as exc:
        print("type:", type(exc).__name__)
        print("msg:", exc)
        print("cause:", type(exc.__cause__).__name__, exc.__cause__)


if __name__ == "__main__":
    main()

建议运行:

python payment_exc_chain.py

期望输出:

type: PaymentCallbackError
msg: invalid callback json
cause: JSONDecodeError Expecting property name enclosed in double quotes: line 1 column 2 (char 1)

结合场景再看三个关注点

  1. 对预期 JSON 语法/字段失败,调用方收到 PaymentCallbackError;显式 cause 保留 JSONDecodeError。except 按当前异常类型匹配,不会自动沿 cause 链捕获底层类型。
  2. JSON 根值也可能是 null、数组或数字;先验证对象形状再读字段。本例只检查 payment_id 是非空字符串,不实现签名校验、重复回调处理或完整支付协议。
  3. 日志应打印链,而不是只 str(exc)。

核心概念与准确模型

层次(节选)

BaseException → Exception → ValueError / TypeError / OSError / RuntimeError …

  • 不要捕获 BaseException(会含 KeyboardInterrupt 等)
  • 库错误优先继承 Exception

链式

形式含义
raise X from e显式 cause
raise X from None抑制隐式上下文的默认展示,__context__ 通常仍可访问
在 except 中 raise X可能带 __context__

边界情况与反直觉行为

  1. from None 设置 __suppress_context__,不会擦除原异常对象;显式 from e 设置 __cause__,默认 traceback 优先展示该因果链。
  2. 多个 except 顺序:先子类后父类。
  3. ExceptionGroup(3.11+)可组合多个 Exception,并不限于并发;普通 except 捕获组对象,except* 按成员类型拆分处理,未处理部分继续传播。

常见误区

[!warning] 常见误区:所有失败都 ValueError 错误理解:省事。
正确模型:调用方需要分支的失败应可区分类型。

工程实践

  • 包内定义 PackageError 基类
  • 边界翻译异常,核心少捕多抛
  • 测试断言类型与 cause

本节总结

层次服务语义,链接服务排障。二者一起才构成可运营错误模型。

自测题

  1. 为什么不建议 except BaseException?
  2. raise X from e 的价值?
参考答案
  1. 会捕获系统退出/中断类异常,干扰正常控制。
  2. 保留根因,便于日志与上层决策。

延伸阅读与资料来源

资料类型支撑内容
Exception hierarchy文档内置树
raise规范from 子句
创建于 2026/7/15 更新于 2026/10/6
Digital Biome
Access

键盘导航

j / k / ↑ / ↓
选择对象
Enter / o
打开选择
/ 或 Ctrl+K
全站搜索
i / f / 1–4
筛选当前列表
g → h / l / p / t / a
首页 / 笔记 / 项目 / 工具 / About
r → 1–9
打开所选对象的关联
u
返回上一页
Esc
关闭浮层 / 取消命令;输入框内清空文本