Skip to content

🎯 高级魔法师实战:异常处理进阶练习

🎉 欢迎来到异常处理进阶的练习环节!通过这些练习,你将巩固Python异常处理的高级特性,掌握自定义异常、上下文管理器、异常链等实用技巧,让你的代码更加健壮可靠。

🎯 练习目标

完成这些练习后,你将能够:

  • 熟练掌握异常的捕获、处理和传播机制
  • 创建和使用自定义异常类及其层次结构
  • 设计和实现自定义上下文管理器
  • 应用异常处理的最佳实践
  • 处理实际应用场景中的异常情况

📝 基础练习

练习1:异常的捕获与处理

任务:编写一个函数 calculate_average(numbers),用于计算列表中所有数字的平均值。函数需要处理以下异常情况:

  1. 如果输入不是列表,抛出 TypeError 异常
  2. 如果列表为空,抛出 ValueError 异常
  3. 如果列表中包含非数字元素,跳过这些元素并继续计算
  4. 如果过滤后没有有效数字,返回 None 并打印提示信息

示例

python
# 正常情况
average = calculate_average([1, 2, 3, 4, 5])
print(average)  # 输出: 3.0

# 包含非数字元素
average = calculate_average([1, 2, "three", 4, 5])
print(average)  # 输出: 3.0 (跳过了"three")

# 空列表
# calculate_average([])  # 应抛出 ValueError 异常

# 非列表输入
# calculate_average(123)  # 应抛出 TypeError 异常

练习2:自定义异常

任务:创建一个自定义异常层次结构,用于处理一个简单的在线商城系统中的错误情况。

  1. 创建基础异常类 ShoppingError
  2. 创建两个子类:ProductErrorOrderError
  3. 为每个子类创建更具体的异常,例如:
    • ProductNotFoundError(找不到商品)
    • ProductOutOfStockError(商品缺货)
    • OrderValidationError(订单验证错误)
    • PaymentError(支付错误)
  4. 实现一个简单的函数 place_order(product_id, quantity),根据不同情况抛出相应的异常

要求:每个自定义异常都应该包含详细的错误信息,并且可以被单独捕获和处理。

练习3:异常链

任务:编写一个函数 read_configuration(filename),用于从配置文件中读取配置信息。函数需要:

  1. 尝试打开并读取指定的文件
  2. 使用 json 模块解析文件内容
  3. 当发生异常时,创建异常链,保留原始异常信息
  4. 处理以下异常情况:
    • 文件不存在 (FileNotFoundError) -> 抛出 ConfigurationError
    • 文件格式错误 (json.JSONDecodeError) -> 抛出 ConfigurationError
    • 权限错误 (PermissionError) -> 抛出 ConfigurationError

提示:使用 raise ... from 语法创建异常链。

练习4:上下文管理器

任务:创建一个自定义上下文管理器 TimerContext,用于测量代码块的执行时间。

要求

  1. 实现 __enter____exit__ 方法
  2. __enter__ 方法中记录开始时间
  3. __exit__ 方法中计算并打印执行时间
  4. 确保即使代码块中发生异常,也能正确计算和显示执行时间

示例

python
with TimerContext("执行计算"):
    result = sum(i * i for i in range(1000000))
# 输出类似于: 执行计算: 0.1234秒

练习5:使用 contextlib 创建上下文管理器

任务:使用 contextlib.contextmanager 装饰器创建一个名为 temporary_directory 的上下文管理器,用于:

  1. 创建一个临时目录
  2. with 语句块中使用这个目录
  3. 无论是否发生异常,在退出 with 语句块时自动删除这个临时目录

提示:使用 tempfileshutil 模块来创建和删除临时目录。

🚀 挑战题

挑战题1:文件操作上下文管理器

任务:创建一个高级文件操作上下文管理器 SafeFile,用于安全地进行文件读写操作。

要求

  1. 支持 readwriteappend 三种模式
  2. 自动处理文件打开和关闭
  3. 提供有用的错误信息
  4. 支持 with 语句的嵌套使用
  5. 实现自动备份功能:当以写模式打开文件时,自动创建文件的备份

示例

python
with SafeFile("example.txt", mode="write") as file:
    file.write("Hello, World!")
    # 如果发生异常,文件仍会被正确关闭,并且原始文件(如果存在)会被备份

挑战题2:数据库事务管理

任务:创建一个数据库事务管理上下文管理器 DatabaseTransaction,用于处理数据库事务。

要求

  1. 支持自动提交和回滚功能
  2. with 语句块内执行的所有操作都在同一个事务中
  3. 如果 with 语句块正常完成,自动提交事务
  4. 如果 with 语句块中发生异常,自动回滚事务
  5. 提供清晰的错误信息和日志记录

示例

python
with DatabaseTransaction(connection) as tx:
    tx.execute("INSERT INTO users (name, email) VALUES (?, ?)", ("Alice", "alice@example.com"))
    tx.execute("UPDATE accounts SET balance = balance - 100 WHERE user_id = ?", (1,))
    # 如果两个操作都成功,自动提交事务;如果任一操作失败,自动回滚

🔧 实践应用

应用场景:API 错误处理

任务:创建一个简单的 API 错误处理系统,用于处理 Web 应用中的各种错误情况。

要求

  1. 创建一个完整的 API 异常层次结构
  2. 实现全局异常处理器,将异常转换为格式化的 JSON 响应
  3. 为不同类型的错误提供不同的 HTTP 状态码和错误信息
  4. 实现错误日志记录功能
  5. 提供详细的错误文档,帮助客户端开发者理解和解决问题

示例结构

python
# 基础 API 异常
class APIException(Exception):
    status_code = 500
    error_code = "SERVER_ERROR"
    message = "服务器内部错误"
    
    def __init__(self, message=None, error_code=None, status_code=None):
        if message: self.message = message
        if error_code: self.error_code = error_code
        if status_code: self.status_code = status_code
        super().__init__(self.message)

# 特定类型的 API 异常
class BadRequestError(APIException):
    status_code = 400
    error_code = "BAD_REQUEST"
    message = "请求参数错误"

class NotFoundError(APIException):
    status_code = 404
    error_code = "NOT_FOUND"
    message = "请求的资源不存在"

# 更多异常类...

# 全局异常处理器(以 Flask 为例)
@app.errorhandler(APIException)
def handle_api_exception(error):
    response = {
        "error": {
            "code": error.error_code,
            "message": error.message
        }
    }
    # 记录错误日志
    logger.error(f"API Error: {error.error_code} - {error.message}", exc_info=True)
    return jsonify(response), error.status_code

💡 部分参考实现

练习1:异常的捕获与处理(参考实现)

python
def calculate_average(numbers):
    if not isinstance(numbers, list):
        raise TypeError("输入必须是列表类型")
    
    if not numbers:
        raise ValueError("列表不能为空")
    
    valid_numbers = []
    for item in numbers:
        try:
            # 尝试将元素转换为浮点数
            valid_numbers.append(float(item))
        except (ValueError, TypeError):
            # 跳过非数字元素
            continue
    
    if not valid_numbers:
        print("警告: 列表中没有有效的数字元素")
        return None
    
    return sum(valid_numbers) / len(valid_numbers)

练习2:自定义异常(参考实现)

python
# 基础异常类
class ShoppingError(Exception):
    """购物系统基础异常"""
    pass

# 商品相关异常
class ProductError(ShoppingError):
    """商品相关异常"""
    pass

class ProductNotFoundError(ProductError):
    """商品未找到异常"""
    def __init__(self, product_id):
        self.product_id = product_id
        message = f"找不到商品 ID: {product_id}"
        super().__init__(message)

class ProductOutOfStockError(ProductError):
    """商品缺货异常"""
    def __init__(self, product_id, requested_quantity, available_quantity):
        self.product_id = product_id
        self.requested_quantity = requested_quantity
        self.available_quantity = available_quantity
        message = f"商品 ID: {product_id} 缺货:  request {requested_quantity}, available {available_quantity}"
        super().__init__(message)

# 订单相关异常
class OrderError(ShoppingError):
    """订单相关异常"""
    pass

class OrderValidationError(OrderError):
    """订单验证错误"""
    def __init__(self, reason):
        self.reason = reason
        message = f"订单验证失败: {reason}"
        super().__init__(message)

class PaymentError(OrderError):
    """支付错误"""
    def __init__(self, payment_method, reason):
        self.payment_method = payment_method
        self.reason = reason
        message = f"{payment_method} 支付失败: {reason}"
        super().__init__(message)

# 简单的订单处理函数
def place_order(product_id, quantity):
    # 模拟商品数据库
    products = {
        1: {"name": "笔记本电脑", "stock": 10},
        2: {"name": "智能手机", "stock": 0},
        # 商品 3 不存在
    }
    
    # 验证商品是否存在
    if product_id not in products:
        raise ProductNotFoundError(product_id)
    
    # 验证商品库存
    available_stock = products[product_id]["stock"]
    if quantity > available_stock:
        raise ProductOutOfStockError(product_id, quantity, available_stock)
    
    # 验证订单数量
    if quantity <= 0:
        raise OrderValidationError("订单数量必须大于零")
    
    # 模拟支付过程
    try:
        # 这里可以添加实际的支付逻辑
        pass
    except Exception as e:
        raise PaymentError("信用卡", str(e))
    
    # 订单成功
    print(f"订单成功: 商品 ID {product_id}, 数量 {quantity}")
    return {"order_id": 12345, "status": "success"}

练习4:上下文管理器(参考实现)

python
import time

class TimerContext:
    def __init__(self, label):
        self.label = label
        self.start_time = None
    
    def __enter__(self):
        self.start_time = time.time()
        return self  # 可以返回任意值,这里返回self供with语句使用
    
    def __exit__(self, exc_type, exc_val, exc_tb):
        end_time = time.time()
        execution_time = end_time - self.start_time
        print(f"{self.label}: {execution_time:.4f}秒")
        
        # 返回False表示异常会继续传播
        return False

📚 学习资源推荐

恭喜你完成了异常处理进阶的所有练习!🎉 通过这些练习,你已经掌握了Python异常处理的高级技巧和最佳实践。在实际项目中,记得根据具体情况选择合适的异常处理策略,创建清晰、有意义的异常层次结构,并始终保持异常处理代码的简洁性和可维护性。 恭喜你完成了异常处理进阶的所有练习!🎉 通过这些练习,你已经掌握了Python异常处理的高级技巧和最佳实践。在实际项目中,记得根据具体情况选择合适的异常处理策略,创建清晰、有意义的异常层次结构,并始终保持异常处理代码的简洁性和可维护性。

异常处理是编写健壮软件的关键技能之一。良好的异常处理不仅可以使你的程序更加稳定可靠,还可以提供更好的用户体验和开发体验。继续加油,将这些知识应用到你的实际项目中吧!🚀

© 2025 技术博客. All rights reserved by 老周有AI