在 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 中禁用断言的主要内容,如果未能解决你的问题,请参考以下文章

断言(编程术语)

断言(assert)

如何在 Visual Studio 2017 中禁用作为调试错误的失败断言?

防御式编程之断言assert的使用

[译]重新思考单元测试断言

如何禁用 pypy 断言语句?