assert仅用于开发测试阶段自检,不可替代if+raise做错误处理;其在优化模式下失效,不校验外部数据,不调用有副作用函数,表达式具懒求值特性,且AssertionError需显式捕获。

assert 不是错误处理机制
它只在 __debug__ 为 True(即未用 -O 或 -OO 启动 Python)时生效,生产环境一关就失效。拿它代替 if + raise 捕获异常,等于埋雷。
常见错误现象:assert x > 0, "x must be positive" 在上线后突然不报错,下游计算直接崩出 ZeroDivisionError 或静默错值。
- 适用场景:仅限开发/测试阶段的快速自检,比如验证函数内部中间状态、算法不变量、单元测试中的临时断言
- 绝不用于校验用户输入、文件内容、网络响应等外部不可控数据
- 不要在 assert 中调用有副作用的函数(如
assert log_user_action(), "must log"),优化模式下这行会被直接删掉
assert 的表达式求值有陷阱
Python 的 assert 是语句,不是函数;它的第二个参数(错误消息)只在断言失败时才求值。但很多人误以为它像 print() 那样“总会执行”。
示例:assert condition, expensive_computation() —— 如果 condition 为真,expensive_computation() 根本不会调用;但如果写成 assert condition, str(expensive_computation()),str() 会强制触发求值,反而破坏了懒求值逻辑。
立即学习“Python免费学习笔记(深入)”;
- 推荐写法:把复杂逻辑提前赋给变量,或改用显式
if not condition: raise AssertionError(msg) - 注意字符串格式化陷阱:
assert x == y, f"expected {y}, got {x}"中,f-string 在断言失败前就全部展开,可能引发意外异常(比如x是 None 且调用了.name) - 性能影响:频繁使用含复杂表达式的
assert会拖慢调试版运行速度,尤其在循环内
和 pytest 的 assert 对比容易混淆
pytest 重写了 assert 语句的行为,让失败时能自动展示变量值、支持自定义解释器插件。但这只是测试框架层面的增强,底层仍是 Python 原生 assert 语句。
关键区别在于:你在 pytest 里写的 assert a == b,看起来像普通断言,其实被 pytest 编译器重写过;而单独运行脚本时,同样的语句没有额外信息输出。
- 不要指望在非 pytest 环境(如直接
python script.py)中获得 pytest 那样的清晰报错 - 如果需要跨环境一致的检查行为,优先用
if not ...: raise ValueError(...) - 兼容性风险:某些静态分析工具(如
pylint)对 pytest 改写后的 assert 识别不准,可能漏报或误报
替代方案比硬扛 assert 更可靠
真正需要“失败即终止+带描述”的地方,raise 明确、可控、可捕获,还支持自定义异常类型。
比如校验配置项:if not isinstance(config, dict): raise TypeError(f"config must be dict, got {type(config).__name__}") —— 这比 assert isinstance(config, dict), "config must be dict" 更稳妥。
-
typing.assert_type()(Python 3.11+)仅用于类型检查器提示,运行时不生效,别当真 - 想保留 assert 形式但又需稳定触发?用
if __debug__: assert ...是徒劳的——__debug__在优化模式下就是False,整块被跳过 - 最常被忽略的一点:assert 失败抛的是
AssertionError,它不继承自Exception,而是继承自BaseException;这意味着except Exception:捕不到它,除非显式写except AssertionError:或except BaseException:










