在 Python 中禁用断言
Posted
技术标签:
【中文标题】在 Python 中禁用断言【英文标题】:Disable assertions in Python 【发布时间】:2010-11-19 09:09:44 【问题描述】:如何?
也就是说,如果一个断言失败,我不希望它抛出一个AssertionError
,而是继续下去。
我该怎么做?
【问题讨论】:
【参考方案1】:在优化模式下运行应该可以做到:
python -OO module.py
【讨论】:
【参考方案2】:使用python -O
:
$ python -O
>>> assert False
>>>
【讨论】:
【参考方案3】:使用 -O 标志调用 Python:
test.py:
assert False
print('Done')
输出:
C:\temp\py>C:\Python26\python.exe test.py
Traceback (most recent call last):
File "test.py", line 1, in <module>
assert(False)
AssertionError
C:\temp\py>C:\Python26\python.exe -O test.py
Done
【讨论】:
【参考方案4】:已经给出的两个答案都是有效的(在命令行上使用-O
或-OO
调用Python)。
来自Python documentation,这里是他们之间的区别:
-O
开启基本优化。这会更改文件扩展名
用于从 .pyc 到 .pyo 的编译(字节码)文件。
-OO
在-O
优化之外丢弃文档字符串。
要检查断言是启用还是禁用,请查看__debug__
的值。
【讨论】:
【参考方案5】:您应该不禁用断言。当注意力转移到别处时,他们会发现意料之外的错误。请参阅"The power of ten"(DOI、Wikipedia)中的规则 5。
编写raise
语句,而不是assert
语句:
if x_is_broken():
raise RuntimeError('`x` is broken.')
无论运行 Python 的优化选项如何,raise
语句仍然存在。此外,使用raise
语句可以指定不同于AssertionError
的异常类型。这对用户非常有用。此外,仅仅写一个raise
语句就会提示自己问自己AssertionError
是否是正确的选择。
此外,在编写raise
语句时,我们被引导编写一条信息性消息,例如raise AssertionError('An error occurred with `x`.')
。在assert
语句中写入错误消息是可能的(例如,assert x, 'An error occurred with `x`.'
,括号可用于写入多行的消息),但是,它可能会被忘记。相比之下,raise AssertionError(....)
要求填写 ....
(而raise AssertionError
的形式不常见,不推荐使用)。
在编写错误消息时,会发现多少进一步的编码错误。
旁注:计算量大的断言检查只能在请求时运行。一种方法是:
import logging
log = logging.getLogger(__name__)
if log.getEffectiveLevel() < logging.DEBUG:
if not check_expensive_property(x):
raise RuntimeError('`x` is broken.')
【讨论】:
// ,但问题是,该语句仍然增加了圈复杂度,而错误处理应该处理其余部分? 如上所保护的断言是昂贵的调用,会显着减慢执行速度。对于某些算法,这种检查可能比整个程序花费的时间长几个数量级。考虑运行相同算法的简单但更简单的实现(因此不太可能包含错误)来检查正确性。或者通过详尽列举一些无法正常运行的内容进行检查。 我认为可读性没有太大问题,因为这样的语句不会在代码中添加嵌套。如果这是一个问题,将其提取为函数调用可以将其移开(我希望这样的重构应该降低圈复杂度)。无论如何,圈复杂度不应支配安全检查。 十个assert的威力和Python的assert不一样【参考方案6】:如何在 Python 中禁用断言?
有多种方法会影响单个进程、环境或单行代码。
我分别演示。
对于整个过程
使用-O
标志(大写O)禁用进程中的所有断言语句。
例如:
$ python -Oc "assert False"
$ python -c "assert False"
Traceback (most recent call last):
File "<string>", line 1, in <module>
AssertionError
请注意,禁用我的意思是它也不执行它后面的表达式:
$ python -Oc "assert 1/0"
$ python -c "assert 1/0"
Traceback (most recent call last):
File "<string>", line 1, in <module>
ZeroDivisionError: integer division or modulo by zero
为了环境
您也可以使用环境变量来设置此标志。
这将影响使用或继承环境的每个进程。
例如,在 Windows 中,设置然后清除环境变量:
C:\>python -c "assert False"
Traceback (most recent call last):
File "<string>", line 1, in <module>
AssertionError
C:\>SET PYTHONOPTIMIZE=TRUE
C:\>python -c "assert False"
C:\>SET PYTHONOPTIMIZE=
C:\>python -c "assert False"
Traceback (most recent call last):
File "<string>", line 1, in <module>
AssertionError
在 Unix 中相同(使用 set 和 unset 来实现各自的功能)
代码中的单点
你继续你的问题:
如果一个断言失败,我不希望它抛出一个 AssertionError,而是继续。
如果您希望执行失败的代码,您可以捕获确保控制流未到达断言,例如:
if False:
assert False, "we know this fails, but we don't get here"
或者你可以捕获断言错误:
try:
assert False, "this code runs, fails, and the exception is caught"
except AssertionError as e:
print(repr(e))
哪个打印:
AssertionError('this code runs, fails, and the exception is caught')
您将从处理AssertionError
的那一刻开始继续前进。
参考
来自the assert
documentation:
这样的断言语句:
assert expression #, optional_message
相当于
if __debug__: if not expression: raise AssertionError #(optional_message)
还有,
内置变量
__debug__
正常情况下为True
,请求优化时为False
(命令行选项-O
)。
还有更多
分配给
__debug__
是非法的。内置变量的值在解释器启动时确定。
来自使用文档:
-O
开启基本优化。这会将已编译(字节码)文件的文件扩展名从 .pyc 更改为 .pyo。另请参阅 PYTHONOPTIMIZE。
和
PYTHONOPTIMIZE
如果设置为非空字符串,则等效 指定
-O
选项。如果设置为整数,则相当于 多次指定-O
。
【讨论】:
在“代码中的单点”的情况下是否可以跳过失败的代码?我尝试将__debug__
设置为 False,但这是不允许的。
@Matthijs 您可以确保控制流不会到达它(例如if False: assert False
),或者您可以捕获断言错误。这些是你的选择。更新了答案以解决您的问题。
感谢您的回答,但还不完全是我的想法。我想在运行时禁用函数内部的断言,最好使用某种上下文管理器:评估断言:foo()
并关闭断言:with skip_assertion(): foo()
。这样做的好处是我不必在函数上添加另一个标志
你可以重写函数的字节码,重写 AST,或者重写函数本身。 (手动或以编程方式,对于任何一个)。重写 AST 可能是最可靠的方法(“简单地”用 Pass
对象替换 Assert
对象)。上下文管理器不会直接为此工作,但您可以拥有某种以这种方式使用修饰函数的机制。无论如何,我不推荐它。我怀疑您想要这样做的原因是您正在调用您无法控制的代码并获得 AssertionErrors。如果是这样,您可能需要找到不同的修复方法。以上是关于在 Python 中禁用断言的主要内容,如果未能解决你的问题,请参考以下文章