错误处理进阶
异常链(Exception Chaining)
Python允许将一个异常链接到另一个异常,这对于保持错误追踪和调试信息非常有用。
# raise from 语法
try:
result = 10 / 0
except ZeroDivisionError as e:
raise ValueError("计算错误") from e
这会创建一个异常链,显示原始异常和新异常之间的关系。
自定义异常链
你可以创建自己的异常类,并使用__cause__属性来链接异常。
class DatabaseError(Exception):
"""数据库错误基类"""
pass
class ConnectionError(DatabaseError):
"""数据库连接错误"""
pass
class QueryError(DatabaseError):
"""数据库查询错误"""
pass
def connect_to_database(url):
try:
# 模拟连接失败
raise ConnectionError("连接超时")
except ConnectionError as e:
raise DatabaseError("数据库连接失败") from e
上下文管理器协议
上下文管理器通过__enter__和__exit__方法实现资源管理。
class FileManager:
def __init__(self, filename, mode):
self.filename = filename
self.mode = mode
self.file = None
def __enter__(self):
self.file = open(self.filename, self.mode)
return self.file
def __exit__(self, exc_type, exc_val, exc_tb):
if self.file:
self.file.close()
return False # 不抑制异常
# 使用上下文管理器
with FileManager('test.txt', 'w') as f:
f.write('Hello, World!')
contextlib模块
Python的contextlib模块提供了更简洁的方式来创建上下文管理器。
from contextlib import contextmanager
@contextmanager
def managed_resource():
"""简单的上下文管理器"""
print("获取资源")
try:
yield "资源"
except Exception as e:
print(f"处理异常: {e}")
raise
finally:
print("释放资源")
# 使用
with managed_resource() as resource:
print(f"使用{resource}")
contextlib实用工具
closing() - 确保资源关闭
from contextlib import closing
class NetworkConnection:
def __init__(self):
self.connected = False
def connect(self):
self.connected = True
print("已连接")
def close(self):
self.connected = False
print("已断开")
def send(self, data):
if self.connected:
print(f"发送数据: {data}")
else:
raise RuntimeError("未连接")
# 使用closing确保资源关闭
with closing(NetworkConnection()) as conn:
conn.connect()
conn.send("Hello")
# 输出:
# 已连接
# 发送数据: Hello
# 已断开
suppress() - 抑制异常
from contextlib import suppress
import os
# 忽略文件不存在的异常
with suppress(FileNotFoundError):
os.remove('不存在的文件.txt')
print("文件已删除")
# 不会抛出异常
redirect_stdout/redirect_stderr
from contextlib import redirect_stdout, redirect_stderr
import io
# 重定向标准输出
f = io.StringIO()
with redirect_stdout(f):
print("这会被捕获")
output = f.getvalue()
print(f"捕获到: {output.strip()}")
异常处理最佳实践
1. 捕获具体异常
# 不好的做法
try:
result = some_function()
except: # 捕获所有异常
pass
# 好的做法
try:
result = some_function()
except ValueError as e:
handle_value_error(e)
except TypeError as e:
handle_type_error(e)
2. 使用finally进行清理
def process_file(filename):
file = None
try:
file = open(filename, 'r')
data = file.read()
return process_data(data)
except FileNotFoundError:
print(f"文件 {filename} 不存在")
finally:
if file:
file.close()
3. 异常分层处理
class AppError(Exception):
pass
class ValidationError(AppError):
pass
class DatabaseError(AppError):
pass
def handle_app_error(error):
if isinstance(error, ValidationError):
print("验证错误")
elif isinstance(error, DatabaseError):
print("数据库错误")
else:
print("未知错误")
实际应用案例
from contextlib import contextmanager
import time
@contextmanager
def timer():
"""计时上下文管理器"""
start = time.time()
try:
yield
finally:
end = time.time()
print(f"执行时间: {end - start:.4f}秒")
# 使用
with timer():
total = sum(range(1000000))
print(f"总和: {total}")
总结
- 异常链有助于追踪错误的根本原因
- 上下文管理器是管理资源的优雅方式
contextlib模块提供了更多实用工具- 始终考虑资源清理和异常处理
- 保持异常处理代码简洁和可读