← 返回首页
🐍

错误处理进阶

📂 python ⏱ 2 min 327 words

异常链(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}")

总结