今日主题异常处理与错误调试一、为什么需要异常处理在程序运行过程中错误是不可避免的文件可能不存在、网络可能超时、用户输入可能无效。如果没有妥善处理程序会直接崩溃。Python 的异常处理机制让我们能够优雅地应对错误保证程序的健壮性。# ❌ 没有异常处理 - 程序会崩溃 def read_file_bad(filename): f open(filename, r) content f.read() f.close() return content # 如果文件不存在直接抛出 FileNotFoundError # read_file_bad(not_exist.txt) # 程序终止二、try-except 基础用法2.1 基本结构def read_file_safe(filename): try: f open(filename, r, encodingutf-8) content f.read() f.close() return content except FileNotFoundError: print(f错误文件 {filename} 不存在) return None except PermissionError: print(f错误没有权限读取 {filename}) return None except Exception as e: print(f发生未知错误{e}) return None # 测试 result read_file_safe(not_exist.txt) print(f返回结果{result}) # 返回 None程序继续运行2.2 捕获多个异常def safe_divide(a, b): try: result a / b return result except (ZeroDivisionError, TypeError) as e: print(f计算错误{e}) return None print(safe_divide(10, 2)) # 5.0 print(safe_divide(10, 0)) # None捕获 ZeroDivisionError print(safe_divide(10, a)) # None捕获 TypeError三、else 和 finally 子句def process_data(data): try: result int(data) print(f转换成功{result}) except ValueError: print(转换失败输入不是有效数字) return None else: # 没有异常时执行 print(✓ 数据验证通过) return result * 2 finally: # 无论是否异常都执行常用于清理资源 print(--- 处理结束 ---) process_data(42) # 输出 # 转换成功42 # ✓ 数据验证通过 # --- 处理结束 --- process_data(abc) # 输出 # 转换失败输入不是有效数字 # --- 处理结束 ---四、主动抛出异常def validate_age(age): if age 0: raise ValueError(年龄不能为负数) if age 150: raise ValueError(年龄不能超过 150 岁) if not isinstance(age, int): raise TypeError(年龄必须是整数) return f年龄验证通过{age}岁 # 测试 try: print(validate_age(25)) # ✓ 年龄验证通过25 岁 print(validate_age(-5)) # 抛出 ValueError except ValueError as e: print(f验证失败{e})五、自定义异常类class InsufficientFundsError(Exception): 余额不足异常 def __init__(self, balance, amount): self.balance balance self.amount amount super().__init__(f余额不足当前余额 {balance}需要 {amount}) class BankAccount: def __init__(self, initial_balance0): self.balance initial_balance def withdraw(self, amount): if amount self.balance: raise InsufficientFundsError(self.balance, amount) self.balance - amount return self.balance def deposit(self, amount): if amount 0: raise ValueError(存款金额必须大于 0) self.balance amount return self.balance # 测试 account BankAccount(100) try: account.withdraw(150) # 触发自定义异常 except InsufficientFundsError as e: print(f取款失败{e}) print(f建议请先存款至少 {e.amount - e.balance} 元)六、实用的调试技巧6.1 记录异常堆栈import traceback import logging # 配置日志 logging.basicConfig( levellogging.ERROR, format%(asctime)s - %(levelname)s - %(message)s ) def risky_operation(): try: # 模拟可能出错的操作 data [1, 2, 3] return data[10] # IndexError except Exception as e: # 记录完整堆栈信息 logging.error(f操作失败{e}) logging.error(traceback.format_exc()) raise # 调用后会看到完整的错误堆栈6.2 上下文管理器处理资源from contextlib import contextmanager contextmanager def open_file_safe(filename, moder): f None try: f open(filename, mode, encodingutf-8) yield f except Exception as e: print(f文件操作出错{e}) raise finally: if f: f.close() print(文件已关闭) # 使用 with open_file_safe(test.txt, w) as f: f.write(Hello World) # 自动关闭文件即使发生异常七、最佳实践总结✅ 推荐做法❌ 避免做法捕获具体异常类型except:捕获所有异常使用finally清理资源忘记关闭文件/连接记录异常堆栈便于调试静默吞掉异常自定义异常表达业务错误滥用通用 Exception尽早抛出晚些捕获过度嵌套 try-except八、实战练习# 练习实现一个安全的 JSON 配置加载器 import json def load_config(filepath): 安全加载 JSON 配置文件 要求 1. 文件不存在时返回默认配置 2. JSON 格式错误时记录日志并返回默认配置 3. 成功时返回解析后的字典 default_config { debug: False, max_connections: 100, timeout: 30 } try: with open(filepath, r, encodingutf-8) as f: config json.load(f) print(f✓ 成功加载配置{filepath}) return config except FileNotFoundError: print(f⚠ 配置文件不存在使用默认配置) return default_config except json.JSONDecodeError as e: print(f⚠ JSON 格式错误{e}使用默认配置) return default_config except Exception as e: print(f⚠ 未知错误{e}使用默认配置) return default_config # 测试 config load_config(config.json) print(f当前配置{config}) 今日要点异常处理是健壮程序的基石- 不要让用户看到崩溃的堆栈精准捕获- 只捕获你预期并能处理的异常资源清理- 用finally或上下文管理器确保资源释放主动抛出- 用异常表达业务逻辑错误记录日志- 生产环境一定要记录异常堆栈
异常处理与错误调试
今日主题异常处理与错误调试一、为什么需要异常处理在程序运行过程中错误是不可避免的文件可能不存在、网络可能超时、用户输入可能无效。如果没有妥善处理程序会直接崩溃。Python 的异常处理机制让我们能够优雅地应对错误保证程序的健壮性。# ❌ 没有异常处理 - 程序会崩溃 def read_file_bad(filename): f open(filename, r) content f.read() f.close() return content # 如果文件不存在直接抛出 FileNotFoundError # read_file_bad(not_exist.txt) # 程序终止二、try-except 基础用法2.1 基本结构def read_file_safe(filename): try: f open(filename, r, encodingutf-8) content f.read() f.close() return content except FileNotFoundError: print(f错误文件 {filename} 不存在) return None except PermissionError: print(f错误没有权限读取 {filename}) return None except Exception as e: print(f发生未知错误{e}) return None # 测试 result read_file_safe(not_exist.txt) print(f返回结果{result}) # 返回 None程序继续运行2.2 捕获多个异常def safe_divide(a, b): try: result a / b return result except (ZeroDivisionError, TypeError) as e: print(f计算错误{e}) return None print(safe_divide(10, 2)) # 5.0 print(safe_divide(10, 0)) # None捕获 ZeroDivisionError print(safe_divide(10, a)) # None捕获 TypeError三、else 和 finally 子句def process_data(data): try: result int(data) print(f转换成功{result}) except ValueError: print(转换失败输入不是有效数字) return None else: # 没有异常时执行 print(✓ 数据验证通过) return result * 2 finally: # 无论是否异常都执行常用于清理资源 print(--- 处理结束 ---) process_data(42) # 输出 # 转换成功42 # ✓ 数据验证通过 # --- 处理结束 --- process_data(abc) # 输出 # 转换失败输入不是有效数字 # --- 处理结束 ---四、主动抛出异常def validate_age(age): if age 0: raise ValueError(年龄不能为负数) if age 150: raise ValueError(年龄不能超过 150 岁) if not isinstance(age, int): raise TypeError(年龄必须是整数) return f年龄验证通过{age}岁 # 测试 try: print(validate_age(25)) # ✓ 年龄验证通过25 岁 print(validate_age(-5)) # 抛出 ValueError except ValueError as e: print(f验证失败{e})五、自定义异常类class InsufficientFundsError(Exception): 余额不足异常 def __init__(self, balance, amount): self.balance balance self.amount amount super().__init__(f余额不足当前余额 {balance}需要 {amount}) class BankAccount: def __init__(self, initial_balance0): self.balance initial_balance def withdraw(self, amount): if amount self.balance: raise InsufficientFundsError(self.balance, amount) self.balance - amount return self.balance def deposit(self, amount): if amount 0: raise ValueError(存款金额必须大于 0) self.balance amount return self.balance # 测试 account BankAccount(100) try: account.withdraw(150) # 触发自定义异常 except InsufficientFundsError as e: print(f取款失败{e}) print(f建议请先存款至少 {e.amount - e.balance} 元)六、实用的调试技巧6.1 记录异常堆栈import traceback import logging # 配置日志 logging.basicConfig( levellogging.ERROR, format%(asctime)s - %(levelname)s - %(message)s ) def risky_operation(): try: # 模拟可能出错的操作 data [1, 2, 3] return data[10] # IndexError except Exception as e: # 记录完整堆栈信息 logging.error(f操作失败{e}) logging.error(traceback.format_exc()) raise # 调用后会看到完整的错误堆栈6.2 上下文管理器处理资源from contextlib import contextmanager contextmanager def open_file_safe(filename, moder): f None try: f open(filename, mode, encodingutf-8) yield f except Exception as e: print(f文件操作出错{e}) raise finally: if f: f.close() print(文件已关闭) # 使用 with open_file_safe(test.txt, w) as f: f.write(Hello World) # 自动关闭文件即使发生异常七、最佳实践总结✅ 推荐做法❌ 避免做法捕获具体异常类型except:捕获所有异常使用finally清理资源忘记关闭文件/连接记录异常堆栈便于调试静默吞掉异常自定义异常表达业务错误滥用通用 Exception尽早抛出晚些捕获过度嵌套 try-except八、实战练习# 练习实现一个安全的 JSON 配置加载器 import json def load_config(filepath): 安全加载 JSON 配置文件 要求 1. 文件不存在时返回默认配置 2. JSON 格式错误时记录日志并返回默认配置 3. 成功时返回解析后的字典 default_config { debug: False, max_connections: 100, timeout: 30 } try: with open(filepath, r, encodingutf-8) as f: config json.load(f) print(f✓ 成功加载配置{filepath}) return config except FileNotFoundError: print(f⚠ 配置文件不存在使用默认配置) return default_config except json.JSONDecodeError as e: print(f⚠ JSON 格式错误{e}使用默认配置) return default_config except Exception as e: print(f⚠ 未知错误{e}使用默认配置) return default_config # 测试 config load_config(config.json) print(f当前配置{config}) 今日要点异常处理是健壮程序的基石- 不要让用户看到崩溃的堆栈精准捕获- 只捕获你预期并能处理的异常资源清理- 用finally或上下文管理器确保资源释放主动抛出- 用异常表达业务逻辑错误记录日志- 生产环境一定要记录异常堆栈