python -o 参数会直接移除 assert 语句,因其将 debug 设为 false,使所有 assert 编译后等价于被删除,不执行、不校验、不抛异常、无运行时开销,且副作用代码(如函数调用)也一并跳过。

python -O 参数会直接移除 assert 语句
Python 解释器在启动时加 -O(大写字母 O,不是零)参数,会把全局常量 __debug__ 设为 False,而所有 assert 语句在编译期就被翻译成类似 if __debug__: ... 的结构。一旦 __debug__ 为 False,整个 assert 行等价于被删掉——不执行、不校验、不抛异常、也不占运行时开销。
常见误判是以为“只是不报错”,其实它连副作用都消失了:
-
assert os.path.exists(config_path)→ 配置文件被删后,程序继续跑,直到后续open()才崩 -
assert user.is_authenticated→ 登录态丢失却无拦截,权限绕过风险暗藏 -
assert do_something() == 42→-O下这行彻底不执行,do_something()的副作用(如发请求、改状态)也一并消失
生产部署中 -O 往往被隐式启用
你以为只在本地手动加了 -O?实际很多生产环境早已默认启用:
- Docker 官方镜像(如
python:3.12-slim)虽不自带-O,但多数 CI/CD 流水线或 k8s 启动命令会显式加上 - gunicorn / uvicorn 默认不透传
-O,但若配置了pythonpath或用exec启动,需检查是否漏传 - 某些 PaaS 平台(如 Heroku、Vercel)底层 Python 运行时可能已启用
-OO(双重优化),连 docstring 都被删,assert 更无从谈起
验证方法很简单:python -O -c "print(__debug__)” 输出 False 即确认生效。
assert 不是输入校验,也不是错误处理机制
它只该用于验证「开发者自己应保证成立」的内部状态,比如:
快速生成专业的 Python 脚本和应用代码。一键创建完整项目结构,支持CLI、API、爬虫、Bot、Django等多种项目类型,包含完整的项目结构、配置文件、依赖管理、测试、README和文档。
-
assert isinstance(data, dict)—— 上游函数契约约定返回 dict,出问题说明你调用错了 assert 0 —— 算法理论值域内,超限说明逻辑有 bug-
assert not self._processed—— 私有方法前置条件,重复调用属于代码误用
而以下写法在线上等于裸奔:
-
assert age >= 18—— 用户输负数或字符串?静默通过 -
assert len(items) == expected—— 外部数据长度突变?断言失效,后续逻辑崩得更晚
真正该做的是 if not condition: raise ValueError(...):不受 -O 影响,可被上层捕获,支持日志追踪,错误类型明确。
测试里用 assert 没问题,但别混淆作用域
Pytest 中的 assert 是安全的,因为测试运行时一般不加 -O,且 pytest 会重写 assert 行,生成带 diff 的详细报告。但要注意:
- 单元测试里的
assert是为了验证行为,不是为了保护线上服务 - 如果测试本身依赖
assert做关键路径校验(比如assert db.query().count() == 1),CI 流水线必须禁用-O,否则测试形同虚设 - 不要在测试里写
assert x is True,改用assert x;浮点比较必须用pytest.approx()
最危险的不是断言失效,而是你忘了它会失效——尤其当 assert 里悄悄混进了日志、缓存更新或状态标记这类副作用代码,-O 一开,bug 变得更难复现、更难定位。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!










