异常处理机制
Python 的异常处理机制赋予程序强大的容错能力。当程序在运行时遇到意外情况(即异常),它不会直接崩溃,而是可以被设计成优雅地处理错误,或继续执行后续逻辑,或按可控方式结束。
在异常发生时,Python 会创建一个异常对象 (exception object),通常是Exception
类的子类实例。不同类型的异常对应不同子类,像除零操作会引发ZeroDivisionError异常
对象,文件读取失败会产生FileNotFoundError
异常对象。
1、try-except-else-finally语句
- try 块:包含可能触发异常的代码。程序会先尝试执行
try
块内的代码。例如,进行文件读取操作时,文件可能不存在,就可将文件读取代码置于try
块中。 - except 块:若
try
块中的代码引发特定类型的异常(或未指定类型时的任何异常),则执行该块代码。捕获异常时,最好指定具体异常类型,这样能针对性处理不同错误。 - else 块(可选):当
try
块中的代码未发生任何异常时,执行此块代码。它用于分离主要操作与操作成功后的后续步骤,让try
块更专注于可能出错部分。
- finally 块(可选):无论
try
块中是否发生异常,此块代码总会执行。常被用于资源清理,如关闭文件、释放数据库连接等。- 模型训练状态记录:模型训练可能非常耗时,如果中途因为各种原因(如内存溢出 OOM、手动中断、硬件故障)停止,希望记录下中断的状态,方便后续恢复。
- 全局状态恢复:恢复全局状态或配置,如果程序在运行过程中修改了全局变量或配置文件,在异常处理结束后,需要恢复到之前的状态或配置。
- 数据库连接关闭:关闭数据库连接,防止资源泄漏。
- 计算资源释放:确保计算资源在使用完毕后被释放,供其他进程或任务使用。
- 资源保存与文件关闭:无论训练成功、失败还是中途被打断,都确保日志文件被正确关闭,避免数据丢失或文件损坏。例如,使用
with
语句来自动管理文件资源(with open
语句),with
语句本身就隐式地使用了类似finally
的机制。
与
if - elif - else
结构不同,if - elif - else
只有一个代码块会执行,而try - except - else
中,try
成功则try
块与else
块都执行,try
失败则执行try
块出错前代码及匹配的except
块代码(else
块不执行)。
2、一些异常报错类型
SyntaxError(语法错误)
原因:代码不符合 Python 的语法规则,解释器在尝试解析代码时就会失败。这种错误在程序运行之前就会被检测到。
示例:
print("--- 1. SyntaxError ---")
# 示例 a: 缺少冒号
# def my_function()
# print("Hello")# 示例 b: 非法表达式
# x = 5 +
# print(x)# 请在演示时逐个取消注释并运行,观察错误
NameError(名称错误)
原因:尝试使用一个未被定义的变量、函数或对象的名称。
示例:
# 示例 a: 变量未定义
# print(some_undefined_variable)
TypeError(类型错误)
原因:对一个不支持该操作的数据类型执行了某个操作或函数。
示例:
# print("Age: " + 25) # 字符串和整数
# my_number = 10
# my_number() # 尝试像函数一样调用一个整数
ValueError(值错误)
原因:函数接收到的参数类型正确,但其值不合适或无效。
示例:
# my_string = "12.34.56"
# number = float(my_string) # '12.34.56' 不是一个有效的浮点数表示
IndexError(索引错误)
原因:尝试访问序列(如列表、元组、字符串)中一个不存在的索引。
示例:
# data = ("apple", "banana")
# print(data[2])
KeyError(键错误)
原因:尝试访问字典中一个不存在的键。
示例:
# student_grades = {"math": 90, "science": 85}
# print(student_grades["history"])
AttributeError(属性错误)
原因:尝试访问一个对象没有的属性或方法。
示例:
# 示例a
# a_string = "hello"
# print(a_string.length) # 字符串长度用 len(a_string),不是 .length 属性# 示例b
# import numpy as np
# arr = np.array([1,2,3])
# print(arr.non_existent_attribute)
ZeroDivisionError(除零错误)
原因:尝试将一个数字除以零。
示例:
# result = 10 / 0
# result
FileNotFoundError(文件未找到错误)
原因:尝试打开一个不存在的文件(通常是在读模式下),或者路径不正确。
示例:
# import pandas as pd
# data = pd.read_csv("hh.csv")
ModuleNotFoundError(导入错误)
原因:尝试导入一个不存在的模块,或者模块存在但其中的特定名称找不到,Python 的模块加载器找不到这个模块。此时通常需要安装对应的库;如果是自定义的模块,则需配置好对应的路径。
# import hhh
当代码出现这类错误时,程序会立即停止执行,并打印出一个 “traceback”(回溯信息)。错误追溯信息是按照函数调用的顺序倒序排列的,以帮助开发者定位错误发生的位置。“most recent call last” 之后会列出最近调用的函数,然后逐步回溯到最初调用的函数。内容包含:
- 错误类型(例如,NameError, TypeError)
- 错误发生的文件名和行号
- 导致错误的那行代码
- 错误的简要描述
示例代码:
print("--- try - except - else 示例 ---")def safe_divide(a, b):print(f"\n尝试计算 {a} / {b}")try:result = a / bexcept ZeroDivisionError:print("错误:除数不能为零!")return None # 或者其他表示失败的值except TypeError:print("错误:输入必须是数字!")return Noneelse:# 只有当try块中的a / b成功执行时,这里才会执行print("除法运算成功!")print(f"结果是: {result}")# 可以在这里进行基于成功结果的进一步操作print(f"结果的两倍是: {result * 2}")return result# 测试用例
safe_divide(10, 2) # 成功
safe_divide(10, 0) # ZeroDivisionError
safe_divide("10", 2) # TypeError (如果我们不先做类型转换的话)
safe_divide(20, "abc") # TypeError
@浙大疏锦行