跳转到主内容
新派编程网 - 新潮编程理念,全新技术课堂!

Python项目中如何自定义异常类_继承Exception基类并添加业务错误码

业务异常必须继承Exception而非BaseException,以确保能被except Exception捕获;自定义异常应通过__init__接收code/message/details等参数,保留args[0]为message,并重写__str__添加错误码,避免异常类爆炸可采用工厂方法+错误码注册表统一管理。 为什么要继承
Exception
而不是
BaseException
直接继承
BaseException
会导致自定义异常无法被常规
except Exception:
捕获,因为
SystemExit
、
KeyboardInterrupt
等系统级异常也继承自它。业务异常必须走
Exception
这条链,否则上线后可能“捕不到错却报错消失”,排查时一头雾水。 常见错误现象:
except Exception:
块完全不触发,日志里只看到未处理的
MyBusinessError
报错退出。 所有业务异常类都应显式继承
Exception
(或其子类如
ValueError
) 避免用
class MyError:
这种无继承写法——Python 3 中默认不继承任何类,等价于
object
,不是异常类型 不要重写
__init__
时不调用
super().__init__(message)
,否则
str(exc)
返回空字符串 如何在自定义异常中安全携带错误码和上下文数据 错误码不能只靠类名区分(比如
UserNotFound
和
OrderNotFound
都是 404),得作为实例属性可读、可序列化。推荐用只读属性 + 初始化参数控制,避免运行时被意外修改。 使用场景:API 返回 JSON 时需透出
code
和
message
;日志采集需统一提取
error_code
字段。 立即学习 “ Python免费学习笔记(深入) ”;
class BizError(Exception): def __init__(self, code: int, message: str, details: dict = None): super().__init__(message) self.code = code self.details = details or {}

使用

raise BizError(code=1001, message="库存不足", details={"sku_id": "S123", "available": 0})

code
用
int
类型,方便前端 switch 判断,也兼容 Prometheus 等监控系统打点 不要把
details
设为可变默认参数
{}
—— 多次 raise 会共享同一字典对象 若需区分错误等级(如警告/错误/严重),可额外加
level
属性,但别混用 logging 级别名(如
"ERROR"
),改用枚举或整数
__str__
和
__repr__
怎么写才不影响调试和日志输出 默认
str(exc)
只返回 message,但运维查日志时往往需要一眼看到
code
。重写
__str__
加上错误码是合理做法;而
__repr__
应保持“可重建对象”的风格,方便开发调试。 Python 3.14.3 微软官方的 Python 扩展,是 VS Code 安装量最高的扩展(209M+)。集成 IntelliSense(通过 Pylance)、调试(通过 Python Debugger)、代码检查、格式化、重构和单元测试等功能。支持 Jupyter Notebook、虚拟环境管理和多 Python 版本切换。 下载 容易踩的坑:重写
__str__
后导致 Sentry 或其他 APM 工具提取 message 失败——它们通常依赖
exc.args[0]
,而非
str(exc)
。 始终保留
message
在
args[0]
,即
super().__init__(message)
不可省
__str__
可返回
f"[{self.code}] {self.args[0]}"
,既清晰又不破坏原有行为
__repr__
建议返回
f"{self.__class__.__name__}({self.code!r}, {self.args[0]!r})"
,复制粘贴到 REPL 能直接执行 如何避免异常类爆炸——用工厂函数或注册表管理业务错误码 项目大了以后,几十个异常类散落在各模块,既难查找又易重复定义(比如两个模块都写了
UserLockedError(2003)
)。与其让每个错误码对应一个类,不如用单个基类 + 类方法生成实例,再配合模块级错误码注册表。 性能影响极小,但能显著降低维护成本,尤其适合微服务间错误码对齐场景。
# errors.py class ErrorCode: USER_NOT_FOUND = 1001 INSUFFICIENT_BALANCE = 1002

class BizError(Exception): _registry = {}

def __init__(self, code: int, message: str, details: dict = None):
    super().__init__(message)
    self.code = code
    self.details = details or {}

@classmethod
def new(cls, code: int, message: str, **kwargs):
    return cls(code, message, kwargs)
使用 raise BizError.new(ErrorCode.USER_NOT_FOUND, "用户不存在") 错误码常量统一收口在
ErrorCode
类或
errors.py
,禁止硬编码数字 不建议用
@unique
枚举类包装错误码——增加 import 依赖,且 IDE 对
Enum
成员的跳转支持不如普通属性 如果团队用 OpenAPI,错误码注释可直接从
ErrorCode
类生成,避免文档和代码脱节 业务异常类本身不复杂,但一旦跨服务、跨语言、进日志系统、对接监控平台,错误码的类型、可读性、不可变性就会变成关键细节。最常被忽略的是
args[0]
的一致性——它决定了 80% 的日志解析和告警规则是否生效。

相关文章