Python的异常处理机制是其核心语言特性之一,也是编写健壮、可维护代码的关键。与C语言通过返回值传递错误不同,Python采用"请求宽恕比请求许可更容易"(EAFP, Easier to Ask for Forgiveness than Permission)的哲学,鼓励开发者大胆执行可能在运行时失败的操作,并在失败发生时优雅地捕获和处理异常。
在某些场景下,底层的实现细节不应暴露给调用者,此时可以使用 raise from None 切断异常链。
class UserNotFoundError(Exception):
pass
def get_user(user_id):
try:
return database.query(f"SELECT * FROM users WHERE id = {user_id}")
except DatabaseConnectionError:
# 调用者不需要知道数据库连接细节
raise UserNotFoundError(f"用户 {user_id} 不可用") from None
最佳实践:在库代码中,推荐使用 raise from 保留完整的错误上下文;仅在确定调用者不需要了解底层技术细节时才使用 raise from None。
import asyncio
async def fetch_url(url, timeout):
if timeout < 0:
raise ValueError(f"无效超时: {timeout}")
# 模拟网络请求
await asyncio.sleep(0.1)
return f"data from {url}"
async def fetch_all(urls):
tasks = [
asyncio.create_task(fetch_url(url, i - 1))
for i, url in enumerate(urls)
]
results = await asyncio.gather(*tasks, return_exceptions=True)
errors = [r for r in results if isinstance(r, Exception)]
successes = [r for r in results if not isinstance(r, Exception)]
if errors:
raise ExceptionGroup("部分请求失败", errors)
return successes
# 领域层/用例层 —— 定义业务异常
class UseCaseError(Exception):
"""所有用例异常的基类"""
pass
class EntityNotFoundError(UseCaseError):
"""实体未找到"""
def __init__(self, entity_type, entity_id):
self.entity_type = entity_type
self.entity_id = entity_id
super().__init__(f"{entity_type} 未找到: {entity_id}")
class BusinessRuleViolation(UseCaseError):
"""业务规则违反"""
pass
# 用例层 —— 不依赖任何基础设施异常
class CreateOrderUseCase:
def __init__(self, order_repo, payment_service):
self._repo = order_repo
self._payment = payment_service
def execute(self, request):
# 业务验证
if request.amount <= 0:
raise BusinessRuleViolation("订单金额必须为正数")
# 领域层不捕获基础设施异常 ——
# 由适配器层负责转译
order = self._repo.save(request.to_order())
return order
7.2 适配器层的异常转译责任
# 适配器层 —— 负责转译基础设施异常
class PostgresOrderRepository(OrderRepository):
def save(self, order):
try:
with self._session.begin():
self._session.add(order)
except IntegrityError as e:
# 将数据库异常转译为领域异常
raise BusinessRuleViolation(
"订单数据违反唯一约束"
) from e
except OperationalError as e:
raise EntityNotFoundError(
"Order", order.id
) from e
import traceback
def inner():
raise ValueError("内部错误")
def middle():
inner()
def outer():
middle()
try:
outer()
except ValueError:
tb = traceback.extract_tb(sys.exception().__traceback__)
print("=== 追踪栈分析 ===")
for frame in tb:
print(f" 文件: {frame.filename}")
print(f" 行号: {frame.lineno}")
print(f" 函数: {frame.name}")
print(f" 代码: {frame.line}")
print(f" 是否源码: {not frame.locals}")
print()
# 过滤:只显示当前项目目录的帧
project_frames = [
f for f in tb
if "/my_project/" in f.filename
]
print(f"项目内帧数: {len(project_frames)}/{len(tb)}")
10.3 print_exception:自定义输出格式
import traceback
def custom_error_report(exception):
"""自定义异常报告格式"""
print("=" * 60)
print(f"[错误报告] {type(exception).__name__}")
print("-" * 60)
# 提取追踪栈
tb = traceback.extract_tb(exception.__traceback__)
# 从外到内打印调用链
for i, frame in enumerate(tb):
indent = " " * i
location = f"{frame.filename}:{frame.lineno}"
print(f"{indent}→ {location}")
print(f"{indent} 在 {frame.name}() 中")
if frame.line:
print(f"{indent} └─ {frame.line.strip()}")
print("-" * 60)
print(f"错误信息: {exception}")
print("=" * 60)
# 使用
try:
# 一些复杂操作
data = {"key": "value"}
data["missing"]["nested"]
except Exception as e:
custom_error_report(e)
10.4 walk_stack 与 walk_tb:生成器形式的遍历
import traceback
def debug_call_stack():
"""打印当前调用栈,用于调试"""
print("=== 当前调用栈 ===")
# 从当前栈帧往上遍历
for frame, lineno in traceback.walk_stack(None):
co = frame.f_code
print(f" {co.co_filename}:{lineno} in {co.co_name}")
if frame.f_locals:
# 显示关键局部变量
for var in ["self", "request", "user_id"]:
if var in frame.f_locals:
print(f" [{var}] = {frame.f_locals[var]}")
def lbyl_divide(a, b):
if not isinstance(a, (int, float)):
return None
if not isinstance(b, (int, float)):
return None
if b == 0:
return None
return a / b
EAFP(先执行再处理)
def eafp_divide(a, b):
try:
return a / b
except (TypeError, ZeroDivisionError):
return None
使用 __suppress_context__ 优化异常链:如果不需要保留完整的异常链,考虑使用 raise from None 减少 traceback 构造开销。
# 不推荐:循环内抛异常
def find_user_bad(users, target_id):
for user in users:
try:
if user.id == target_id:
return user
except AttributeError:
pass # user 没有 id 属性
return None
# 推荐:前置检查
def find_user_good(users, target_id):
for user in users:
if not hasattr(user, 'id'):
continue
if user.id == target_id:
return user
return None
十二、核心要点总结
异常链(raise from):使用 raise ... from ... 保留完整的异常因果链;使用 raise from None 仅在确定需要隐藏实现细节时使用。