Python 自定义异常
目录
学习目标
- 掌握自定义异常类的创建方法
- 理解异常继承的最佳实践
- 学会在项目中构建异常层次结构
- 理解异常的传递和包装
1. 创建自定义异常
自定义异常需要继承自 Exception 或其子类。
1.1 最简单的自定义异常
class ValidationError(Exception):
"""数据验证错误"""
pass
# 使用
raise ValidationError("年龄必须在 0-150 之间")1.2 带参数的自定义异常
class BusinessError(Exception):
"""业务逻辑错误"""
def __init__(self, message, code=None):
super().__init__(message)
self.code = code
self.message = message
def __str__(self):
if self.code:
return f"[{self.code}] {self.message}"
return self.message
# 使用
try:
raise BusinessError("用户不存在", code="USER_NOT_FOUND")
except BusinessError as e:
print(e) # [USER_NOT_FOUND] 用户不存在
print(e.code) # USER_NOT_FOUND
print(e.message) # 用户不存在2. 异常层次结构
在项目中建立清晰的异常层次结构,便于调用者精确捕获。
# 基类 - 所有应用异常的根
class AppException(Exception):
"""应用异常基类"""
def __init__(self, message, code="UNKNOWN"):
super().__init__(message)
self.code = code
self.message = message
# 业务异常
class BusinessException(AppException):
"""业务逻辑异常"""
def __init__(self, message, code="BUSINESS_ERROR"):
super().__init__(message, code)
# 数据异常
class DataException(AppException):
"""数据相关异常"""
def __init__(self, message, code="DATA_ERROR"):
super().__init__(message, code)
# 具体业务异常
class UserNotFoundException(BusinessException):
"""用户不存在"""
def __init__(self, user_id):
super().__init__(f"用户 {user_id} 不存在", "USER_NOT_FOUND")
self.user_id = user_id
class InvalidPasswordException(BusinessException):
"""密码错误"""
def __init__(self):
super().__init__("密码不正确", "INVALID_PASSWORD")
class DatabaseConnectionException(DataException):
"""数据库连接失败"""
def __init__(self, host):
super().__init__(f"无法连接到数据库: {host}", "DB_CONNECTION_ERROR")
self.host = host2.1 使用层次异常
def get_user(user_id):
"""获取用户"""
if user_id <= 0:
raise UserNotFoundException(user_id)
return {"id": user_id, "name": "用户" + str(user_id)}
def login(username, password):
"""登录"""
if password != "correct":
raise InvalidPasswordException()
return {"token": "abc123"}
# 统一处理
def handle_request():
try:
user = get_user(999)
result = login(user["name"], "wrong")
except UserNotFoundException as e:
print(f"用户错误 [{e.code}]: {e.message}")
except InvalidPasswordException as e:
print(f"认证错误 [{e.code}]: {e.message}")
except BusinessException as e:
print(f"业务错误 [{e.code}]: {e.message}")
except AppException as e:
print(f"应用错误 [{e.code}]: {e.message}")
except Exception as e:
print(f"系统错误: {e}")
handle_request()3. 异常包装
3.1 将底层异常转换为业务异常
import json
class ConfigError(AppException):
"""配置错误"""
pass
def load_config(filename):
"""加载配置文件"""
try:
with open(filename, 'r') as f:
return json.load(f)
except FileNotFoundError as e:
raise ConfigError(f"配置文件不存在: {filename}") from e
except json.JSONDecodeError as e:
raise ConfigError(f"配置文件格式错误: {e}") from e
except PermissionError as e:
raise ConfigError(f"无权读取配置文件: {filename}") from e
# 使用
try:
config = load_config('app.json')
except ConfigError as e:
print(f"配置加载失败: {e}")
if e.__cause__:
print(f"原始错误: {type(e.__cause__).__name__}: {e.__cause__}")3.2 API 异常处理模式
class APIError(Exception):
"""API 错误"""
def __init__(self, message, status_code=500, details=None):
super().__init__(message)
self.status_code = status_code
self.details = details or {}
def to_dict(self):
return {
"error": self.message,
"code": self.status_code,
"details": self.details
}
class NotFoundError(APIError):
def __init__(self, resource, resource_id):
super().__init__(
f"{resource} {resource_id} 不存在",
status_code=404
)
class ValidationError(APIError):
def __init__(self, field, message):
super().__init__(
"数据验证失败",
status_code=400,
details={"field": field, "message": message}
)
# 模拟 API 处理
def api_handler():
try:
# 模拟各种错误
raise NotFoundError("用户", 123)
except APIError as e:
return e.to_dict()
print(api_handler())
# {'error': '用户 123 不存在', 'code': 404, 'details': {}}4. 实际项目中的异常处理
4.1 完整的异常模块
# exceptions.py
class AppError(Exception):
"""应用基础异常"""
status_code = 500
error_code = "INTERNAL_ERROR"
def __init__(self, message=None):
self.message = message or "服务器内部错误"
super().__init__(self.message)
def to_response(self):
return {
"success": False,
"error_code": self.error_code,
"message": self.message
}
class NotFoundError(AppError):
"""资源不存在"""
status_code = 404
error_code = "NOT_FOUND"
def __init__(self, resource="资源"):
super().__init__(f"{resource}不存在")
class BadRequestError(AppError):
"""请求参数错误"""
status_code = 400
error_code = "BAD_REQUEST"
def __init__(self, message="请求参数错误"):
super().__init__(message)
class UnauthorizedError(AppError):
"""未授权"""
status_code = 401
error_code = "UNAUTHORIZED"
def __init__(self, message="请先登录"):
super().__init__(message)
class ForbiddenError(AppError):
"""禁止访问"""
status_code = 403
error_code = "FORBIDDEN"
def __init__(self, message="无权访问"):
super().__init__(message)4.2 使用示例
# service.py
from exceptions import NotFoundError, BadRequestError
class UserService:
users = {1: {"name": "Alice"}, 2: {"name": "Bob"}}
@classmethod
def get_user(cls, user_id):
if not isinstance(user_id, int) or user_id <= 0:
raise BadRequestError("用户ID必须是正整数")
user = cls.users.get(user_id)
if not user:
raise NotFoundError(f"用户(ID={user_id})")
return user
@classmethod
def create_user(cls, name):
if not name or len(name) < 2:
raise BadRequestError("用户名至少需要2个字符")
new_id = max(cls.users.keys()) + 1
cls.users[new_id] = {"name": name}
return cls.users[new_id]
# 使用
try:
user = UserService.get_user(999)
print(user)
except NotFoundError as e:
print(f"404: {e.to_response()}")
except BadRequestError as e:
print(f"400: {e.to_response()}")5. 异常与上下文信息
5.1 添加上下文
class ContextualError(Exception):
"""带上下文的异常"""
def __init__(self, message, **context):
super().__init__(message)
self.context = context
def __str__(self):
base = super().__str__()
if self.context:
context_str = ", ".join(f"{k}={v}" for k, v in self.context.items())
return f"{base} (上下文: {context_str})"
return base
# 使用
def process_order(order_id, user_id):
raise ContextualError(
"订单处理失败",
order_id=order_id,
user_id=user_id,
timestamp="2024-01-01"
)
try:
process_order(1001, 42)
except ContextualError as e:
print(e)
print(f"上下文: {e.context}")本节小结
- 自定义异常:继承
Exception或其子类 - 层次结构:建立从通用到具体的异常树
- 异常包装:将底层异常转换为业务异常,
raise ... from保留链路 - 实际应用:为 API、业务逻辑设计统一的异常体系
- 上下文信息:在异常中附加额外信息便于排查问题
练习
- 为你的项目设计一套三层异常体系(应用级、模块级、具体错误)
- 创建一个
RetryableError异常,标记可以重试的操作错误 - 实现一个装饰器,自动将函数中的异常转换为指定的自定义异常
- 编写一个异常转换函数,将 Python 内置异常映射为你的业务异常