自定义业务异常应继承Exception类而非BaseException,最简实现为空类;需携带上下文时重写__init__保存属性并调用super().__init__(message);可通过__str__定制用户提示,__repr__辅助调试;异常类应保持轻量,避免逻辑处理与敏感信息暴露。

继承 Exception 类是最直接的方式
Python 中所有异常都继承自 BaseException,但自定义业务异常应继承 Exception(而非 BaseException),否则会拦截系统退出、键盘中断等关键信号。
最简实现就是定义一个空类,它自动继承 __init__ 和 __str__ 行为:
class ValidationError(Exception):
pass
<p>raise ValidationError("用户名不能为空")
</p>
这样抛出的异常会显示完整错误消息,且能被 except ValidationError: 精确捕获。
重写 __init__ 以支持结构化参数
如果需要在异常中携带额外上下文(比如字段名、原始值),不要只拼接字符串,而是把信息存为实例属性。这样上层可做条件判断或日志增强:
- 避免覆盖父类
__init__时不调用super().__init__(...),否则str(exc)为空 - 推荐把用户可见消息传给父类,其他数据作为独立属性保存
- 不要在
__init__中做耗时操作(如网络请求、文件读取)
class ValidationError(Exception):
def __init__(self, message, field=None, value=None):
super().__init__(message) # 确保 str() 可用
self.field = field
self.value = value
<p>exc = ValidationError("长度不能超过20", field="username", value="abc" * 10)
print(str(exc)) # "长度不能超过20"
print(exc.field) # "username"
</p>
用 __str__ 或 __repr__ 控制打印格式
默认情况下,print(exc) 显示的是 __str__ 返回值。如果想让异常对象在调试时更易读,可以定制它:
-
__str__应返回简洁、面向用户的错误描述(如日志输出) -
__repr__更适合开发调试,可包含类名和关键属性(如ValidationError(field='email', value='@')) - 不建议在
__str__中拼接敏感数据(如密码原文、token)
class ValidationError(Exception):
def __init__(self, message, field=None):
super().__init__(message)
self.field = field
<pre class="brush:php;toolbar:false;">def __str__(self):
if self.field:
return f"[{self.field}] {super().__str__()}"
return super().__str__()
raise ValidationError("格式错误", field="email")
输出:[email] 格式错误
避免在异常类里做类型检查或逻辑分支
异常类本质是数据容器 + 少量展示逻辑,不是业务处理器。常见误用包括:
- 在
__init__中根据value类型决定抛什么异常(该由调用方判断) - 重写
__init__时对参数做复杂校验(增加异常构造开销) - 让异常类自己记录日志或上报监控(耦合严重,应由异常处理方统一做)
真正需要差异化行为的地方,应该用不同异常子类,而不是在同一个类里 if-else 分支。
自定义异常的关键不是“能加多少功能”,而是“能否让捕获方清晰区分错误类型、安全提取上下文、不意外改变程序流”。字段名、错误码、原始值这些信息是否暴露,得看调用链下游是否真需要——多数时候,多一个 self.code = 400 就够了,别堆砌属性。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











