自定义封装Exception异常,使用 raise...from

1、基础篇:try...except 的基本用法

try...except 是 Python 中最基本的异常处理结构。它的工作原理是:尝试执行 try 块中的代码,如果发生异常,则跳转到匹配的 except 块进行处理。

def safe_divide(a, b):
    try:
        # 尝试进行除法运算
        result = a / b
    except ZeroDivisionError:
        # 捕获除零错误,打印友好提示并返回None
        print("错误:除数不能为零!")
        return None
    # 如果没有异常发生,返回正常计算结果
    return result


# 测试用例1:正常除法
print(safe_divide(10, 2))  # 输出: 5.0
# 测试用例2:触发除零错误
print(safe_divide(10, 0))  # 输出: 错误:除数不能为零!然后返回 None

捕获多个异常类型也很简单,只需在 except 语句中指定多个异常类型,或者使用多个 except 块:  

def process_data(data):
    try:
        value = int(data)
        reciprocal = 1 / value
    except (ValueError, TypeError):
        # 捕获数值转换错误(如输入"abc")或类型错误(如输入None)
        print("输入必须是有效的数字")
    except ZeroDivisionError:
        # 捕获除零错误(当输入为0时)
        print("输入不能为零")


# 测试用例1:输入非数字字符串
process_data("abc")  # 输出: 输入必须是有效的数字
# 测试用例2:输入数字0
process_data(0)  # 输出: 输入不能为零

  

2、进阶篇:else 和 finally 的妙用

除了 try 和 except,Python 还提供了 else 和 finally 子句来完善异常处理逻辑。else 块中的代码仅在 try 块没有引发异常时执行,而 finally 块中的代码无论是否发生异常都会执行。

下面是一个文件处理的例子,展示了如何正确使用 else 和 finally:

def read_file(filename):
    file = None
    try:
        file = open(filename, 'r')
        content = file.read()
    except FileNotFoundError:
        print(f"文件 {filename} 不存在")
    except IOError:
        print(f"读取文件 {filename} 时发生错误")
    else:
        print("文件读取成功")
        return content
    finally:
        if file is not None:
            file.close()
            print("文件已关闭")


read_file("example.txt")

finally 块特别适合用于资源清理工作,如关闭文件、释放锁、断开数据库连接等。即使 try 块中发生了异常且未被捕获,或者有 return 语句,finally 块也会被执行。

 

3、异常对象和自定义异常

Python 中的异常实际上是对象,我们可以访问异常对象来获取更多错误信息。每个异常对象通常都有描述性的消息,可以通过 str() 函数或 args 属性访问。

try:
    x = 1 / 0
except ZeroDivisionError as e:
    print(f"发生错误: {e}")  # 输出: 发生错误: division by zero
    print(f"错误参数: {e.args}")  # 输出: 错误参数: ('division by zero',)

当内置异常类型不能满足需求时,我们可以创建自定义异常。自定义异常应该继承自 Exception 类或其子类:

# 自定义异常类,用于处理无效输入的情况
class InvalidInputError(Exception):
    """当输入不符合预期时抛出"""
    def __init__(self, input_value, message="无效的输入"):
        self.input_value = input_value  # 存储引发异常的输入值
        self.message = message  # 自定义错误消息
        super().__init__(self.message)  # 调用父类Exception的初始化方法


# 年龄验证函数
def validate_age(age):
    """验证年龄是否在合理范围内"""
    if age < 0 or age > 120:  # 检查年龄是否在0-120之间
        # 如果不在范围内,抛出自定义异常
        raise InvalidInputError(age, "年龄必须在0-120之间")
    return age  # 如果验证通过,返回年龄值


# 测试代码
try:
    validate_age(150)  # 尝试验证一个超出范围的年龄
except InvalidInputError as e:
    # 捕获自定义异常并打印详细信息
    print(f"错误: {e.message}, 输入值: {e.input_value}")

  

4、异常处理的用法

异常处理虽然强大,但滥用会导致代码难以理解和维护。以下是几个重要的最佳实践:

  1. 只捕获你能处理的异常:不要使用裸露的 except 语句捕获所有异常,这可能会掩盖真正的问题。
  2. 保持异常处理代码简洁:except 块应该专注于错误恢复,避免在其中包含过多业务逻辑。
  3. 使用特定的异常类型:捕获最具体的异常类型,而不是通用的 Exception。
  4. 合理使用异常链:Python 3 引入了异常链,可以使用 raise...from 语法保留原始异常信息。

下面是一个展示这些最佳实践的示例:

import requests  # 定义一个从URL获取数据的函数


def fetch_data(url):
    try:
        response = requests.get(url, timeout=5)
        # 如果响应状态码不是200,抛出HTTPError异常
        response.raise_for_status()

    # 捕获超时异常
    except requests.exceptions.Timeout as e:
        # 将超时异常转换为更明确的ConnectionError并保留原始异常信息
        raise ConnectionError("服务器响应超时") from e

    # 捕获HTTP错误异常
    except requests.exceptions.HTTPError as e:
        # 特殊处理404错误
        if e.response.status_code == 404:
            raise ValueError("请求的资源不存在") from e
        # 其他HTTP错误直接重新抛出
        raise

    # 捕获其他请求相关的异常
    except requests.exceptions.RequestException as e:
        # 转换为更通用的连接错误
        raise ConnectionError("网络请求失败") from e

    # 如果没有异常发生
    else:
        # 返回解析后的JSON数据
        return response.json()


# 使用示例
try:
    # 尝试获取API数据
    data = fetch_data("https://api.example.com/data")
# 捕获资源不存在的错误
except ValueError as e:
    print(f"客户端错误: {e}")
# 捕获所有连接相关的错误
except ConnectionError as e:
    print(f"连接问题: {e}")

  

5、异常追踪和日志记录

当异常发生时,Python 会生成详细的回溯信息。理解这些信息对于调试至关重要。我们可以使用 traceback 模块来获取和处理回溯信息。同时,将异常信息记录到日志中比简单打印到控制台更加专业和实用:

import logging
import traceback

logging.basicConfig(filename='app.log', level=logging.ERROR)


def process_item(item):
    try:
        # 处理逻辑
        value = int(item['value'])
    except (KeyError, ValueError) as e:
        logging.error(f"处理项目失败: {e}\n{traceback.format_exc()}")
        raise
    else:
        return value * 2

  

 

posted @ 2025-06-30 10:44  北京测试菜鸟  阅读(20)  评论(0)    收藏  举报