python基础入门:6.3异常处理机制

发布于:2025-02-12 ⋅ 阅读:(139) ⋅ 点赞:(0)

Python异常处理全面指南:构建健壮程序的关键技术

# 完整异常处理模板
def process_file(file_path):
    """文件处理示例函数"""
    file = None
    try:
        file = open(file_path, 'r', encoding='utf-8')
        data = json.load(file)
      
        if not data:
            raise EmptyDataError("文件内容为空")  # 自定义异常
          
        result = complex_processing(data)  # 可能抛出多种异常
      
    except FileNotFoundError as e:
        logging.error(f"文件不存在: {e}")
        raise  # 重新抛出异常
      
    except json.JSONDecodeError as e:
        raise InvalidFormatError("JSON格式错误") from e
      
    except EmptyDataError as e:
        logging.warning(e)
        return None
      
    else:
        logging.info("文件处理成功")
        return result
      
    finally:
        if file is not None:
            try:
                file.close()
            except IOError as e:
                logging.error(f"关闭文件失败: {e}")
一、异常处理结构详解
  1. 完整结构流程图
开始
↓
try
├─ 可能抛出异常的代码
↓
except
├─ 捕获指定异常
↓
else (可选)
├─ 无异常时执行
↓
finally (可选)
└─ 始终执行的清理代码
  1. 执行顺序示例
def division_test(value):
    try:
        print("try块开始")
        result = 10 / value
    except ZeroDivisionError:
        print("捕获除零错误")
    except TypeError:
        print("捕获类型错误")
    else:
        print("计算结果:", result)
    finally:
        print("finally块执行")
      
# 测试用例
division_test(2)  # 正常执行
division_test(0)  # 触发ZeroDivisionError
division_test("a") # 触发TypeError
  1. 各代码块使用场景
代码块 典型用途 最佳实践
try 包裹可能出错的代码 尽量缩小代码范围
except 处理特定异常 明确指定异常类型,避免裸露except
else 执行依赖try成功的操作 将与try相关的后续操作放在else中
finally 资源清理(文件/网络连接) 确保关闭资源的代码必须放在此处
二、自定义异常与异常链
  1. 自定义异常类模板
class AppBaseError(Exception):
    """应用基础异常类"""
    def __init__(self, message="应用错误", code=1000):
        self.code = code
        self.message = message
        super().__init__(self.message)

class NetworkError(AppBaseError):
    """网络相关异常"""
    def __init__(self, url, status_code):
        super().__init__(
            message=f"网络请求失败: {url} (状态码: {status_code})",
            code=2001
        )
        self.url = url
        self.status_code = status_code

class DatabaseError(AppBaseError):
    """数据库操作异常"""
    ERROR_CODES = {
        1452: "外键约束失败",
        1062: "重复键值"
    }
  
    def __init__(self, sql, errcode):
        code = 3000 + errcode
        msg = f"SQL执行错误: {self.ERROR_CODES.get(errcode, '未知错误')}"
        super().__init__(message=msg, code=code)
        self.sql = sql
  1. 异常链实践
def fetch_data():
    try:
        response = requests.get('https://api.example.com/data', timeout=5)
        response.raise_for_status()
        return response.json()
    except requests.RequestException as e:
        raise DataFetchError("数据获取失败") from e  # 保留原始异常信息

try:
    data = fetch_data()
except DataFetchError as e:
    print(f"错误原因: {e}")
    print(f"原始异常: {e.__cause__}")  # 显示底层网络异常
三、断言与防御性编程
  1. 断言使用规范
def calculate_discount(price, discount):
    """计算商品折扣价"""
    # 前置条件检查
    assert isinstance(price, (int, float)), "价格必须是数值"
    assert 0 <= discount <= 1, "折扣率应在0-1之间"
  
    # 业务逻辑
    discounted = price * (1 - discount)
  
    # 后置条件验证
    assert discounted <= price, "折后价不应超过原价"
    return discounted

# 测试断言
try:
    calculate_discount(100, 0.2)  # 正常
    calculate_discount("100", 0.5) # 触发类型断言
except AssertionError as e:
    print(f"输入验证失败: {e}")
  1. 防御性编程技巧
def safe_divide(a, b):
    """安全的除法运算"""
    # 输入验证
    if not isinstance(a, (int, float)) or not isinstance(b, (int, float)):
        raise TypeError("操作数必须是数值类型")
  
    # 边界条件检查
    if b == 0:
        raise ValueError("除数不能为零")
  
    # 类型转换
    result = float(a) / float(b)
  
    # 结果验证
    if math.isinf(result):
        raise OverflowError("计算结果溢出")
  
    return round(result, 4)
四、异常处理最佳实践
  1. 异常处理决策树
出现错误情况
↓
是否属于正常业务流程? → 是 → 使用返回码/状态标识
↓
否
↓
是否可恢复? → 是 → 捕获处理
↓
否
↓
抛出异常终止流程
  1. 异常日志规范
import logging
import traceback

logger = logging.getLogger(__name__)

try:
    risky_operation()
except (NetworkError, DatabaseError) as e:
    logger.error(f"操作失败 [代码:{e.code}]: {e.message}")
except Exception as e:
    logger.critical(
        f"未处理的异常: {str(e)}\n"
        f"堆栈跟踪: {traceback.format_exc()}"
    )
    raise
  1. 性能优化建议
  • 避免在频繁执行的代码路径中使用try块
  • 将异常处理移到循环外部
  • 使用预检查替代异常捕获(LBYL vs EAFP)
# 低效方式
for num in numbers:
    try:
        value = 10 / num
    except ZeroDivisionError:
        value = 0

# 高效方式
processed = []
for num in numbers:
    if num == 0:
        processed.append(0)
    else:
        processed.append(10/num)

异常处理反模式

# 错误1:吞噬所有异常
try:
    do_something()
except:
    pass

# 错误2:过于宽泛的异常捕获
try:
    process_data()
except Exception as e:
    handle_error()

# 错误3:重复的异常处理
try:
    file.read()
except IOError:
    file.close()
  
try:
    file.write()
except IOError:
    file.close()

# 正确方式:使用上下文管理器
with open('file.txt') as f:
    process(f)

调试建议

# 在开发环境启用详细调试
if DEBUG:
    import pdb
    pdb.set_trace()

# 使用__debug__优化生产代码
if __debug__:
    print("调试信息")
else:
    logging.info("生产日志")
开始执行
可能出错?
使用try包裹
正常流程
发生异常?
匹配except块
记录日志
处理/转换异常
是否需要中断流程
抛出新异常
恢复执行
执行else块
正常返回结果
外层捕获处理
最终清理
结束

网站公告

今日签到

点亮在社区的每一天
去签到