自定义封装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、异常处理的用法
异常处理虽然强大,但滥用会导致代码难以理解和维护。以下是几个重要的最佳实践:
- 只捕获你能处理的异常:不要使用裸露的 except 语句捕获所有异常,这可能会掩盖真正的问题。
- 保持异常处理代码简洁:except 块应该专注于错误恢复,避免在其中包含过多业务逻辑。
- 使用特定的异常类型:捕获最具体的异常类型,而不是通用的 Exception。
- 合理使用异常链: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

浙公网安备 33010602011771号