【问题标题】:Disable assertions in Python在 Python 中禁用断言
【发布时间】:2010-11-19 09:09:44
【问题描述】:

如何在 Python 中禁用断言?

也就是说,如果一个断言失败,我不希望它抛出一个AssertionError,而是继续下去。

我该怎么做?

【问题讨论】:

    标签: python debugging exception-handling environment-variables assert


    【解决方案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"DOIWikipedia)中的规则 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。如果是这样,您可能需要找到不同的修复方法。
            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 2016-11-05
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多