@浙大疏锦行
📘 Day 25 实战作业:Python 异常处理 —— 提升代码健壮性
1. 作业综述
核心目标:
本作业旨在掌握 Python 异常处理的“四大金刚”机制(try-except-else-finally)。我们将通过模拟真实的数据处理和模型训练场景,学习如何编写“抗造”的代码,防止程序因意外错误(如文件丢失、除零、手动中断)而崩溃。
作业背景:
在深度学习和数据科学项目中,代码往往需要运行数小时甚至数天。如果因为一个微小的格式错误或网络波动导致程序直接中断,成本是非常高昂的。
此外,现阶段的大模型(如 GPT-4)生成的代码为了保证运行成功率,非常喜欢使用异常处理结构。理解这一机制,能让你更好地阅读和维护 AI 辅助生成的代码。
涉及知识点:
- 异常捕获:
try-except基本结构与多重捕获。 - 逻辑分流:
else子句的使用(仅在无异常时执行)。 - 资源清理:
finally子句的使用(无论是否出错必执行,如保存模型、关闭文件)。 - 实战应用: 模拟模型训练中的“断点续存”保护。
步骤 1:基础异常捕获
场景描述:
在数据处理中,经常会遇到除零错误(ZeroDivisionError)或 类型不匹配(TypeError)。直接运行会导致程序中断,我们需要捕获这些错误并给出友好提示或默认值。
任务:
- 编写一个函数
safe_division(a, b),尝试计算a / b。 - 捕获
ZeroDivisionError,当除数为 0 时,打印提示并返回None。 - 捕获
TypeError,当输入不是数字时,打印提示并返回None。
defsafe_division(a,b):print(f"--- 正在尝试计算{a}/{b}---")try:result=a/bprint(f"计算成功:{result}")returnresultexceptZeroDivisionError:print("❌ 错误:除数不能为 0!")returnNoneexceptTypeError:print(f"❌ 错误:输入类型不对!收到的是{type(a).__name__}和{type(b).__name__}")returnNone# --- 测试用例 ---safe_division(10,2)# 正常safe_division(5,0)# 除零错误safe_division(10,"a")# 类型错误--- 正在尝试计算 10 / 2 --- 计算成功: 5.0 --- 正在尝试计算 5 / 0 --- ❌ 错误:除数不能为 0! --- 正在尝试计算 10 / a --- ❌ 错误:输入类型不对!收到的是 int 和 str步骤 2:进阶结构 (try-except-else-finally)
场景描述:
在实际项目中,比如文件读取或数据库操作,我们往往需要更精细的控制:
- try: 尝试打开文件并读取数据。
- except: 如果文件不存在,捕获错误。
- else: 如果读取成功,进行数据处理(将业务逻辑与错误处理分离)。
- finally: 无论成功与否,都要关闭文件句柄,释放资源。
任务:
模拟一个文件读取流程,展示完整异常处理结构的执行顺序。
defprocess_file_data(filename):print(f"\n📁 开始处理文件:{filename}")file_handle=Nonetry:# 1. 尝试打开文件 (模拟)iffilename=="missing.txt":raiseFileNotFoundError("模拟:文件未找到")eliffilename=="corrupt.txt":# 模拟打开了文件但读取出错file_handle="Open_File_Handle"raiseValueError("模拟:文件内容格式错误")# 模拟正常情况file_handle="Open_File_Handle"print("✅ [Try] 文件打开成功,正在读取数据...")data=[1,2,3,4,5]exceptFileNotFoundErrorase:# 2. 捕获文件不存在错误print(f"❌ [Except] 捕获错误:{e}")exceptValueErrorase:# 3. 捕获数据格式错误print(f"❌ [Except] 捕获错误:{e}")else:# 4. 只有 try 成功且无异常时执行print(f"✨ [Else] 读取成功,开始计算平均值:{sum(data)/len(data)}")finally:# 5. 无论如何都会执行 (通常用于清理资源)iffile_handle:print("🔒 [Finally] 关闭文件句柄,释放资源。")else:print("🔒 [Finally] 没有打开的文件需要关闭。")print("--- 处理结束 ---")# --- 测试不同场景 ---process_file_data("data.txt")# 正常流程process_file_data("missing.txt")# 文件丢失process_file_data("corrupt.txt")# 文件损坏📁 开始处理文件: data.txt ✅ [Try] 文件打开成功,正在读取数据... ✨ [Else] 读取成功,开始计算平均值: 3.0 🔒 [Finally] 关闭文件句柄,释放资源。 --- 处理结束 --- 📁 开始处理文件: missing.txt ❌ [Except] 捕获错误: 模拟:文件未找到 🔒 [Finally] 没有打开的文件需要关闭。 --- 处理结束 --- 📁 开始处理文件: corrupt.txt ❌ [Except] 捕获错误: 模拟:文件内容格式错误 🔒 [Finally] 关闭文件句柄,释放资源。 --- 处理结束 ---步骤 3:实战应用 —— 模拟模型训练中断保护
场景描述:
在深度学习训练中,可能会因为显存溢出(OOM)或 手动中断(KeyboardInterrupt)导致程序停止。
利用try-except-finally,我们可以在异常发生时保存当前的检查点(Checkpoint),避免白跑几个小时。
任务:
模拟一个简单的训练循环,捕获手动中断信号(KeyboardInterrupt),并在中断时保存模型状态。
importtimedefmock_training_loop(epochs=5):print(f"\n🚀 开始训练模型,共{epochs}轮...")current_epoch=0try:forepochinrange(1,epochs+1):current_epoch=epochprint(f" > 正在训练第{epoch}/{epochs}轮...",end="\r")# 模拟训练耗时time.sleep(0.5)# 模拟在第 3 轮发生意外中断 (例如用户按 Ctrl+C)ifepoch==3:print("\n⚠️ 模拟:收到中断信号 (KeyboardInterrupt)!")raiseKeyboardInterruptprint(f" > 第{epoch}轮完成。 ")exceptKeyboardInterrupt:print("\n🛑 [Except] 捕获到手动中断!正在停止训练...")exceptExceptionase:print(f"\n❌ [Except] 发生未知错误:{e}")finally:# 无论正常结束还是被中断,都保存当前进度print(f"💾 [Finally] 安全保存模型状态 (Epoch{current_epoch})...")print("✅ 资源清理完毕,程序安全退出。")# --- 运行模拟 ---mock_training_loop()🚀 开始训练模型,共 5 轮... > 第 1 轮完成。 > 第 2 轮完成。 > 正在训练第 3/5 轮... ⚠️ 模拟:收到中断信号 (KeyboardInterrupt)! 🛑 [Except] 捕获到手动中断!正在停止训练... 💾 [Finally] 安全保存模型状态 (Epoch 3)... ✅ 资源清理完毕,程序安全退出。🎓 Day 25 总结:让代码更“抗造”
今天我们学习了 Python 的异常处理机制,这是从“写脚本”转向“写工程”的重要一步。
- 防御性编程:不要假设输入永远是正确的,文件永远是存在的,网络永远是通畅的。
- 结构化处理:
try: 核心逻辑。except: 错误兜底。else: 成功后的进一步操作(逻辑解耦)。finally: 无论生死,必须完成的清理工作(如关闭文件、保存模型)。
- AI 编程习惯:正如课程开头所说,AI 生成的代码为了保证健壮性,常大量使用这种结构。理解它,你就能更好地利用 AI 辅助开发。
Next Level: 掌握了异常处理,我们的代码质量上了一个台阶。接下来我们将继续深入更多高级特性!