在不使用 -O 标志的情况下禁用 python 的 assert()
我正在一个软件里运行一个Python脚本(这个软件提供了一个Python接口,可以用来操作它的数据结构)。
我正在优化我的代码,希望看看我的断言(asserts)对性能的影响。
我无法使用 python -O
命令。那我还有什么其他方法可以在代码中程序化地禁用所有的断言呢?变量 __debug__
(在使用 -O
标志时会被清除)是不能被赋值的 :(
3 个回答
正如@unutbu所说,这里没有官方的做法。不过,有一个简单的办法,就是在某个地方定义一个标志,比如叫_test
(可以作为函数的关键字参数,或者作为模块中的全局变量)。然后在你的断言语句中包含这个标志,像这样:
def f(x, _test=True):
assert not _test or x > 0
...
这样的话,如果需要,你就可以在那个函数里禁用断言了。
f(x, _test=False)
你可以通过设置环境变量来实现这个功能,具体可以参考这个回答。设置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”。
文档中说,
内置变量 [
__debug__
] 的值是在解释器启动时决定的。
所以,如果你无法控制 Python 解释器的启动方式,那么看起来你就无法禁用 assert 语句。
这里有一些其他的选择:
- 最安全的方法是手动删除所有的 assert 语句。
如果你的所有 assert 语句都是单独占一行的,那么你可以尝试用下面的命令来删除它们:
sed -i 's/assert /pass #assert /g' script.py
请注意,如果 assert 后面还有其他代码,这样做会搞乱你的代码。例如,上面的命令会把像下面这样的行中的
return
注释掉:assert x; return True
这会改变你程序的逻辑。
如果你的代码是这样的,最好还是手动删除 assert 语句。
也许可以通过使用
tokenize
模块来解析你的脚本,从而编程地删除 assert 语句,但写这样一个程序可能需要的时间比手动删除还要长,特别是如果这只是一次性的工作。如果其他软件可以接受 .pyc 文件,那么有一个小技巧在我的机器上似乎有效,不过请注意 一位 Python 核心开发者对此表示警告(见 Éric Araujo 在 2011-09-17 的评论)。假设你的脚本叫
script.py
。先创建一个临时脚本,比如叫 temp.py:
import script
- 运行
python -O temp.py
。这会生成script.pyo
。 - 把
script.py
和script.pyc
(如果存在的话)移动到你的 PYTHONPATH 之外,或者移动到其他软件读取脚本的目录之外。 - 把
script.pyo
重命名为script.pyc
。
现在,当其他软件尝试导入你的脚本时,它只会找到删除了 assert 的
pyc
文件。例如,如果
script.py
看起来像这样:assert False print('Got here')
那么运行
python temp.py
现在会打印Got here
,而不是引发 AssertionError。