当前位置:首页>python>Python错误处理与调试5个技巧:写出更健壮的代码

Python错误处理与调试5个技巧:写出更健壮的代码

  • 2026-10-11 05:53:04
Python错误处理与调试5个技巧:写出更健壮的代码

异常处理不是事后补救,而是代码健壮性的第一道防线。这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或finally
with open(...)
业务逻辑错误
自定义异常类
class OrderError
调试信息
logging.debug
logger.debug(...)
输入验证
断言 + 类型提示
assert x > 0
外部依赖
记录堆栈,优雅降级
logger.error(..., exc_info=True)
未知错误
捕获后记录,重新抛出
except Exception: logger.error(...); raise

八、常见错误模式与修复

错误模式问题修复方案
裸except
隐藏错误,难以调试
指定具体异常类型
吞掉异常
except: pass
至少记录日志
过度捕获
捕获Exception
只捕获你知道如何处理的
延迟清理
异常时资源泄漏
使用with语句
错误信息不清
print("错误")
包含上下文信息
忽略返回值
调用不检查结果
检查函数返回值

九、总结

  1. 精确捕获
    只捕获你知道如何处理的异常
  2. finally清理
    确保资源正确释放
  3. 自定义异常
    让错误语义更清晰
  4. logging代替print
    生产环境必备
  5. 断言+类型提示
    在早期发现错误

错误处理:

  • Fail-fast:尽早发现错误
  • Fail-loudly:让错误可见,不要静默失败
  • Fail-gracefully:优雅降级,保证系统稳定

最新文章

随机文章