异常处理不是事后补救,而是代码健壮性的第一道防线。这5个技巧让你从"代码能跑"进化到"代码靠谱"。
一、技巧1:精确捕获异常,不要裸except
1.1 错误示范
try: result = 10 / int(input("请输入除数:"))except: print("出错了") # 到底是什么错?try: while True: passexcept: print("永远不会执行到这里")
1.2 正确示范
try: divisor = int(input("请输入除数:")) result = 10 / divisorexcept ValueError: print("请输入有效的整数")except ZeroDivisionError: print("除数不能为0")except Exception as e: print(f"未知错误:{e}") raise # 重新抛出,不要 swallowed
1.3 捕获多个异常
try: file = open('data.txt') content = file.read() value = int(content)except (FileNotFoundError, PermissionError) as e: print(f"文件错误:{e}")except ValueError: print("文件内容不是有效的整数")
二、技巧2:使用finally和else优化资源管理
2.1 try-except-finally模式
file = Nonetry: file = open('important_data.txt', 'r') data = file.read() process(data)except FileNotFoundError: print("文件不存在")except Exception as e: print(f"处理失败:{e}")finally: if file: file.close() print("文件已关闭")
2.2 with语句(推荐)
try: with open('important_data.txt', 'r') as file: data = file.read() process(data)except FileNotFoundError: print("文件不存在")
2.3 try-else模式
try: result = risky_operation()except SomeException: handle_error()else: print(f"操作成功,结果:{result}") save_result(result)finally: cleanup()
else块的优势:明确区分"正常逻辑"和"异常处理逻辑"。
***
三、技巧3:自定义异常类
3.1 为什么需要自定义异常?
def validate_age(age): if age < 0: raise Exception("年龄无效") # 太笼统 if age > 150: raise Exception("年龄无效") # 同样的异常,不同原因try: validate_age(-5)except Exception as e: if "年龄无效" in str(e): pass
3.2 自定义异常类
class AppError(Exception): """应用基础异常""" passclass ValidationError(AppError): """验证错误""" def __init__(self, field, message): self.field = field super().__init__(f"{field}: {message}")class AgeError(ValidationError): """年龄相关错误""" passclass RangeError(AgeError): """年龄超出范围""" def __init__(self, age, min_age=0, max_age=150): self.age = age super().__init__( "age", f"年龄{age}超出范围[{min_age}, {max_age}]" )def validate_age(age): if age < 0: raise RangeError(age, min_age=0) if age > 150: raise RangeError(age, max_age=150) return Truetry: validate_age(-5)except RangeError as e: print(f"范围错误:{e}") print(f"错误字段:{e.field}")except ValidationError as e: print(f"验证错误:{e}")
3.3 异常层次设计
AppError (基类)├── ValidationError (验证错误)│ ├── AgeError│ ├── NameError│ └── EmailError├── DatabaseError (数据库错误)│ ├── ConnectionError│ └── QueryError└── BusinessError (业务错误) ├── InsufficientStockError └── PaymentFailedError
***
四、技巧4:使用logging代替print调试
4.1 为什么不用print?
4.2 logging基本使用
import logginglogging.basicConfig( level=logging.INFO, format='%(asctime)s - %(name)s - %(levelname)s - %(message)s', handlers=[ logging.FileHandler('app.log'), logging.StreamHandler() ])logger = logging.getLogger(__name__)logger.debug("调试信息") # 开发时使用logger.info("普通信息") # 重要流程节点logger.warning("警告信息") # 预期内的错误logger.error("错误信息") # 需要关注的错误logger.critical("严重错误") # 系统崩溃前兆
4.3 模块化日志
auth_logger = logging.getLogger('app.auth')db_logger = logging.getLogger('app.database')auth_logger.info("用户登录成功:%s", username)db_logger.error("查询失败:%s", sql)
4.4 异常日志记录
import loggingimport tracebacklogger = logging.getLogger(__name__)def process_order(order_id): try: order = db.get_order(order_id) validate(order) payment = process_payment(order) send_confirmation(order) except ValidationError as e: logger.warning("订单验证失败:%s", e) raise except PaymentError as e: logger.error("支付失败:order_id=%s, error=%s", order_id, e) logger.debug("堆栈信息:%s", traceback.format_exc()) raise except Exception as e: logger.critical("未预期的错误:%s", e, exc_info=True) raise
***
五、技巧5:使用断言和类型提示预防错误
5.1 断言(assert)
def calculate_discount(price, discount_rate): """计算折扣后价格""" assert price > 0, f"价格必须为正数,当前值:{price}" assert 0 <= discount_rate <= 1, f"折扣率必须在[0,1]之间,当前值:{discount_rate}" return price * (1 - discount_rate)calculate_discount(100, 0.2) # 正常
5.2 类型提示 + 静态检查
from typing import Optionaldef find_user(user_id: int) -> Optional[dict]: """根据ID查找用户""" user = db.query("SELECT * FROM users WHERE id = %s", user_id) return user # 可能返回Noneuser = find_user(123)print(user['name']) # mypy报错!可能为Noneuser = find_user(123)if user is not None: print(user['name'])else: print("用户不存在")
5.3 使用pydantic进行运行时验证
from pydantic import BaseModel, EmailStr, Field, validatorclass OrderCreate(BaseModel): customer_id: int = Field(..., gt=0) amount: float = Field(..., gt=0, lt=1000000) items: list = Field(..., min_items=1) email: EmailStr @validator('items') def validate_items(cls, v): if not all(item.get('quantity', 0) > 0 for item in v): raise ValueError("商品数量必须为正") return vtry: order = OrderCreate( customer_id=123, amount=-100, # 会报错! items=[{'id': 1, 'quantity': 2}], email="invalid" )except ValidationError as e: print(e.json()) # 详细的错误信息
***
六、调试技巧进阶
6.1 使用pdb调试
def complex_calculation(data): result = [] for item in data: import pdb; pdb.set_trace() processed = process(item) result.append(processed) return result
6.2 使用ipdb(增强版pdb)
pip install ipdb
import ipdbdef buggy_function(): ipdb.set_trace() # 支持Tab补全、语法高亮
6.3 使用logging.debug()
def process_data(data): logging.debug(f"输入数据:{data}") result = [] for item in data: logging.debug(f"处理项目:{item}") processed = transform(item) logging.debug(f"处理结果:{processed}") result.append(processed) logging.debug(f"最终结果:{result}") return result
***
七、错误处理最佳实践总结
| 场景 | 推荐做法 | 示例 |
|---|
| | except ValueError |
| | with open(...) |
| | class OrderError |
| | logger.debug(...) |
| | assert x > 0 |
| | logger.error(..., exc_info=True) |
| | except Exception: logger.error(...); raise |
八、常见错误模式与修复
九、总结
- 精确捕获
- finally清理
- 自定义异常
- logging代替print
- 断言+类型提示
错误处理:
- Fail-gracefully:优雅降级,保证系统稳定