上线前同事把一堆 assert 写在接口参数校验里,说什么环境都能过。我提醒他一句,服务是用 -O 参数跑的,这些断言全被 Python 优化没了,等于没校验。他当场愣住。

assert 是什么

assert 是个断言语句,不是函数,虽然括号长得像函数。写法是 assert 条件, 提示信息,条件为真什么都不做,为假就抛 AssertionError:

assert len(data) > 0, '数据不能为空'

它存在的意义是开发期抓 bug,把不可能出现的情况标出来,让错误尽早暴露。

为什么不能做参数校验

用 python -O 或者 python -OO 启动时,解释器会把所有 assert 整条删掉,连带着提示信息一起没了。所以拿 assert 当校验,等于在不同的启动方式下行为不一致,这种校验形同虚设。线上要拦非法输入,老老实实写 if 加 raise。要区分就记住:assert 用于内部不变式,raise 用于对外部输入的防御。

几种常见写法

  • 单条件:assert x > 0,留意这是语句不是函数
  • 带信息:assert x, 'x 必须为正数'
  • 元组陷阱:assert (x, 'msg') 永远为真,因为非空元组恒真
  • 测试里:单测推荐直接用 assert,框架会重写它给出更好的报错

和 unittest 的差别

unittest 里的 self.assertEqual 会给出期望值和实际值的对比,报错更友好。pytest 里普通 assert 就够了,pytest 会自己解析表达式。写业务代码用 assert 抓逻辑错误,写测试用框架自带断言方法,分工别混。

我的建议:assert 摆在那些「按设计绝不该发生」的位置,当程序内部的自检哨兵;所有来自用户、网络、文件的数据,一律用 if 加异常处理。