在不使用 -O 标志的情况下禁用 python 的 assert()

13 投票
3 回答
5289 浏览
提问于 2025-04-17 09:09

我正在一个软件里运行一个Python脚本(这个软件提供了一个Python接口,可以用来操作它的数据结构)。

我正在优化我的代码,希望看看我的断言(asserts)对性能的影响。

我无法使用 python -O 命令。那我还有什么其他方法可以在代码中程序化地禁用所有的断言呢?变量 __debug__(在使用 -O 标志时会被清除)是不能被赋值的 :(

3 个回答

0

正如@unutbu所说,这里没有官方的做法。不过,有一个简单的办法,就是在某个地方定义一个标志,比如叫_test(可以作为函数的关键字参数,或者作为模块中的全局变量)。然后在你的断言语句中包含这个标志,像这样:

def f(x, _test=True):
    assert not _test or x > 0
    ...

这样的话,如果需要,你就可以在那个函数里禁用断言了。

f(x, _test=False)
1

你可以通过设置环境变量来实现这个功能,具体可以参考这个回答。设置PYTHONOPTIMIZE=1就相当于用-O选项启动Python。举个例子,这在嵌入Python 3.5的Blender 2.78中是有效的:

blender --python-expr 'assert False; print("foo")'

PYTHONOPTIMIZE=1 blender --python-expr 'assert False; print("foo")'

第一个命令会打印出错误追踪信息,而第二个命令只会打印“foo”。

10

文档中说

内置变量 [__debug__] 的值是在解释器启动时决定的。

所以,如果你无法控制 Python 解释器的启动方式,那么看起来你就无法禁用 assert 语句。

这里有一些其他的选择:

  1. 最安全的方法是手动删除所有的 assert 语句。
  2. 如果你的所有 assert 语句都是单独占一行的,那么你可以尝试用下面的命令来删除它们:

    sed -i 's/assert /pass #assert /g' script.py
    

    请注意,如果 assert 后面还有其他代码,这样做会搞乱你的代码。例如,上面的命令会把像下面这样的行中的 return 注释掉:

    assert x; return True
    

    这会改变你程序的逻辑。

    如果你的代码是这样的,最好还是手动删除 assert 语句。

  3. 也许可以通过使用 tokenize 模块来解析你的脚本,从而编程地删除 assert 语句,但写这样一个程序可能需要的时间比手动删除还要长,特别是如果这只是一次性的工作。

  4. 如果其他软件可以接受 .pyc 文件,那么有一个小技巧在我的机器上似乎有效,不过请注意 一位 Python 核心开发者对此表示警告(见 Éric Araujo 在 2011-09-17 的评论)。假设你的脚本叫 script.py

    • 先创建一个临时脚本,比如叫 temp.py:

      import script
      
    • 运行 python -O temp.py。这会生成 script.pyo
    • script.pyscript.pyc(如果存在的话)移动到你的 PYTHONPATH 之外,或者移动到其他软件读取脚本的目录之外。
    • script.pyo 重命名为 script.pyc

    现在,当其他软件尝试导入你的脚本时,它只会找到删除了 assert 的 pyc 文件。

    例如,如果 script.py 看起来像这样:

    assert False
    print('Got here')
    

    那么运行 python temp.py 现在会打印 Got here,而不是引发 AssertionError。

撰写回答