python 在 -O 模式下触发 assert

4 投票
1 回答
1067 浏览
提问于 2025-04-17 23:43

我想确保在使用 -O 选项时,Python 不会执行 assert 语句。但是我的测试程序显示,assert 语句总是被执行。我在命令行中明确使用了 -O,并且在运行 setup.py 的时候,无论是构建还是安装,我也都用了 -O。在我提交错误报告之前,我想确认一下自己没有犯什么低级错误……

那么,我需要做些什么或者有什么不同的方式,才能确保 assert 语句 被执行呢?

这是我的简单脚本:

print __debug__

if __debug__:
    print "True branch"
else:
    print "False branch"

assert(False)

在独立运行时可以正常工作。输出是:

False
False branch

当我把这段代码复制到主程序中(我不能在这里包含主程序的代码……)时,我得到了:

False
True branch
AssertionError

我对此感到非常困惑。这是在 Mac 上运行的 Python 2.7.6。(抱歉用的是 Mac,我工作需要用它。)

1 个回答

4

你可以通过直接运行一个 .pyc 文件并加上 -O 这个标志,来实现你想要的效果。不过,这样做其实是有点不对劲的。你应该选择以下其中一种方式:

  1. 运行 .py 文件,可以加上或不加 -O 标志(这是常见的做法),或者
  2. 运行 .pyc 文件,但不要加 -O 标志,或者
  3. 运行 .pyo 文件,并加上 -O 标志。

如果你用 -O 标志运行 .pyc 文件,或者用不加标志的方式运行 .pyo 文件,你可能会遇到一些意想不到的情况。

发生的事情是,在编译时,优化器会把 if __debug__ 相关的代码给优化掉,所以 .pyc.pyo 文件会无条件地执行某个分支。然后当你用错误的 -O 设置运行时,__debug__ 的值就和编译时的优化不匹配了。

之前在 Python 的问题追踪器上也有一个类似的问题,不过那是相反的情况:有人在没有使用 -O 标志的情况下运行 .pyo 文件。

举个简单的例子:假设我有一个文件,名字叫 "debug_example.py",就在我当前的目录下:

noether:Desktop mdickinson$ cat debug_example.py
def main():
    print "__debug__ is {}".format(__debug__)
    if __debug__:
        print "__debug__ is True"
    else:
        print "__debug__ is False"

if __name__ == '__main__':
    main()

如果我们直接执行这个文件,不管加不加 -O 标志,都会看到预期的结果:

noether:Desktop mdickinson$ python2 debug_example.py
__debug__ is True
__debug__ is True
noether:Desktop mdickinson$ python2 -O debug_example.py
__debug__ is False
__debug__ is False

现在我们用方便的 py_compile 模块把这个文件编译成 "debug_example.pyc" 文件。(在你的情况下,这个编译可能是在 setup.py 安装过程中进行的):

noether:Desktop mdickinson$ python2 -m py_compile debug_example.py
noether:Desktop mdickinson$ ls -l debug_example.pyc
-rw-r--r--  1 mdickinson  staff  350 24 Mar 21:41 debug_example.pyc

现在我们执行 debug_example.pyc 文件,但(错误地)加上了 -O 标志,结果 Python 就搞糊涂了:

noether:Desktop mdickinson$ python2 -O debug_example.pyc
__debug__ is False
__debug__ is True

我们可以使用 Python 的 dis 模块 来查看模块内部的字节码:

Python 2.7.6 (default, Nov 18 2013, 15:12:51) 
[GCC 4.2.1 Compatible Apple LLVM 5.0 (clang-500.2.79)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import debug_example
>>> import dis
>>> dis.dis(debug_example)
Disassembly of main:
  2           0 LOAD_CONST               1 ('__debug__ is {}')
              3 LOAD_ATTR                0 (format)
              6 LOAD_GLOBAL              1 (__debug__)
              9 CALL_FUNCTION            1
             12 PRINT_ITEM          
             13 PRINT_NEWLINE       

  4          14 LOAD_CONST               2 ('__debug__ is True')
             17 PRINT_ITEM          
             18 PRINT_NEWLINE       
             19 LOAD_CONST               0 (None)
             22 RETURN_VALUE        

注意,这里根本没有与 if 语句对应的字节码:我们看到的是无条件打印 '__debug__ is True'

解决办法是:不要直接执行 .pyc.pyo 文件,而是执行 .py 文件,让 Python 自己决定是否使用 .pyc.pyo 文件。

撰写回答