Python 异常层次与异常链

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

#type / concept #status / growing #tech / dev #resource / python #tech / lang / python

[!info] 关联笔记

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 "payment_id" not in data:
        raise PaymentCallbackError("missing payment_id")
    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. 对外类型稳定(PaymentCallbackError),对内仍能见 JSONDecodeError。
  2. 缺字段可不带 cause,本身就是领域问题。
  3. 日志应打印链,而不是只 str(exc)

核心概念与准确模型

层次(节选)

BaseExceptionExceptionValueError / TypeError / OSError / RuntimeError

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

链式

形式含义
raise X from e显式 cause
raise X from None抑制上下文
在 except 中 raise X可能带 __context__

边界情况与反直觉行为

  1. from None 会丢掉调试线索,仅在确认噪声时使用。
  2. 多个 except 顺序:先子类后父类。
  3. 异常组 ExceptionGroup(3.11+)用于并发多错误。

常见误区

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

工程实践

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

本节总结

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

自测题

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

延伸阅读与资料来源

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