python -O 会彻底移除 assert 字节码,而非跳过;因 debug 设为 False,AST 编译阶段即丢弃整个 assert 行及其条件(含副作用),无运行时开销。

python -O 会直接移除 assert 字节码,不是“跳过”,而是彻底不存在
Python 解释器启动时加 -O 参数,会把全局常量 __debug__ 设为 False,而所有 assert 语句在 AST 编译阶段就被当作 if __debug__: 块处理。一旦 __debug__ 为 False,整个 assert 行连同其条件表达式(包括副作用)都会被丢弃——不会求值、不抛异常、也不占运行时开销。
常见误判:
- 以为
assert x > 0在生产环境只是“不报错”,其实x > 0根本没执行 - 在
assert里调用日志或状态更新函数,比如assert log_and_update() == True,-O下连日志都不会打 - Docker 或 gunicorn 启动时没透传
-O,导致部分服务有断言、部分没有,行为不一致
assert 不是运行时校验,而是开发期契约快照
assert 的设计目标很明确:只用于验证「开发者应保证成立」的内部状态,比如算法中间结果、私有方法前置条件、枚举分支穷尽性等。它面向的是人,不是用户输入或外部依赖。
典型适用场景:
立即学习“Python免费学习笔记(深入)”;
-
assert isinstance(result, dict)—— 检查上游函数返回类型是否符合内部约定 assert 0 —— 验证归一化计算后值域未越界-
assert False放在if/elif末尾,表示该分支理论上不可达
但以下写法在线上等于裸奔:
微软官方的 Python 扩展,是 VS Code 安装量最高的扩展(209M+)。集成 IntelliSense(通过 Pylance)、调试(通过 Python Debugger)、代码检查、格式化、重构和单元测试等功能。支持 Jupyter Notebook、虚拟环境管理和多 Python 版本切换。
-
assert os.path.exists(config_file)—— 配置文件被删,程序继续跑直到open()报错 -
assert user_id > 0—— 用户传负数或字符串,校验直接消失 -
assert request.method == "POST"—— HTTP 方法非法,无拦截直接进业务逻辑
用 if + raise 替代 assert 才能确保生产环境有效
真正需要在线上生效的校验,必须显式写出判断逻辑,并抛出可捕获、可分类、可记录的异常。
实操建议:
- 输入校验统一用
if not isinstance(x, int): raise TypeError(...),别总用Exception - 高频路径(如 API 入口)避免在
if条件里做 DB 查询或网络请求 - 错误信息要带上下文:
raise ValueError(f"user_id={user_id!r} must be positive integer") - 对核心状态机迁移等关键路径,定义业务异常类(如
StateTransitionError),而非堆砌ValueError
最容易被忽略的坑:你以为它还在起作用
最危险的不是断言失效,而是团队成员在 assert 里悄悄塞了副作用逻辑,或者把 assert 当成默认开启的防护层。一旦 -O 生效,这些逻辑连同副作用一起蒸发,问题往往延迟暴露、难以复现。
更隐蔽的风险点:
- CI 流水线用
python -O跑测试,却没发现某些测试用例实际依赖assert存活 - 本地开发用
pytest,它默认启用增强断言(--assert=rewrite),掩盖了线上真实行为 - 日志里突然看不到
AssertionError,第一反应是“bug 修好了”,其实是被-O静默吞掉了








