add_note 是 Python 3.11 引入的异常方法,用于在不改变类型和 traceback 的前提下向异常追加调试说明;适用于捕获后补充上下文、测试中注入诊断线索等场景。

add_note 是什么,它能解决什么问题
add_note 是 Python 3.11 引入的异常对象方法,用于在不改变异常类型和原始 traceback 的前提下,向异常实例追加人类可读的调试说明。它不是替代 raise ... from 或 except 块中的日志,而是给已经抛出(或即将抛出)的异常“贴便签”——比如标注数据来源、上下文状态、临时修复尝试等。
典型适用场景:在 except 块中捕获异常后,想补充一句“这个请求来自用户 ID 12345”,但又不想用 raise new_exc from old_exc 破坏原始调用栈;或者在测试中动态注入诊断线索,方便快速定位。
怎么正确调用 add_note,常见错误有哪些
必须对异常实例调用,不能对异常类、字符串或普通对象调用;且只能在异常被创建之后、被抛出之前(或被捕获之后)使用。最常踩的坑是:在 raise 语句后调用(此时异常已离开作用域),或对未捕获的异常对象误以为还能修改。
-
add_note只接受一个str参数,传入非字符串会触发TypeError - 对已抛出的异常(如未捕获的顶层异常)无法再调用
add_note—— 它不是魔法,只是给BaseException实例加了个__notes__列表属性 - 如果在
except中重新raise同一异常对象(如raise无参数),add_note添加的内容会被保留;但如果raise e且e是新构造的异常,则丢失
正确示例:
立即学习“Python免费学习笔记(深入)”;
try:
int("not_a_number")
except ValueError as e:
e.add_note("原始输入来自 config.yaml 第 42 行")
raise # 保留原异常,note 会出现在 traceback 末尾
add_note 在 traceback 中如何显示,和 __cause__ / __context__ 有什么区别
当异常最终未被捕获并打印 traceback 时,add_note 的内容会显示在 traceback 底部,以 note: 开头,独立成行,位置在 __cause__(显式链式异常)之后、__context__(隐式链式异常)之前。它不参与异常传播逻辑,不影响 except 匹配,也不改变异常类型或 args。
-
__cause__由raise ... from设置,表示“此异常是由另一个异常直接导致的”,会强制出现在 traceback 中并影响控制流 -
__context__是 Python 自动设置的隐式上下文(如在except块中又抛出新异常),同样参与异常链 -
add_note只是附加文本,不构成异常链,不触发任何自动行为,纯属调试辅助
所以如果你看到 traceback 里有 note: 行,那一定是某处代码明确调用了 add_note,而不是框架或解释器自动加的。
兼容性与实际使用建议
add_note 仅存在于 Python 3.11+,旧版本调用会报 AttributeError。不要试图用 hasattr(e, "add_note") 做运行时降级——因为降级方案(比如拼接进 args)会污染原始错误信息,反而让排查更难。
- 生产环境慎用:避免把敏感信息(如 token、密码、完整路径)写进
add_note,它会随 traceback 泄露 - 单元测试中很实用:可在 fixture 或
pytest.raises捕获后动态加 note,说明当前测试用例的特殊条件 - 注意日志系统兼容性:某些日志库(如
structlog)可能忽略__notes__,需确认是否提取该字段
真正容易被忽略的是:它只对当前异常实例生效,不会继承,也不会被子类异常自动携带。如果封装了自定义异常类,记得在 __init__ 里手动处理 notes 参数,否则上游调用 add_note 后,你重写的 __str__ 可能根本没展示它。


















