我可以assert
抛出一个我选择而不是的异常AssertionError
吗?
更新:
我将解释我的动机:到目前为止,我已经进行了断言式测试,这些测试引发了我自己的异常;例如,当您Node
使用某些参数创建一个对象时,它会检查参数是否适合创建节点,如果不是,它会 raise NodeError
。
但我知道 Python 有一种-o
跳过断言的模式,我希望有这种模式,因为它可以让我的程序更快。但我仍然希望有自己的例外。这就是为什么我想在我自己的异常中使用断言。
这将起作用。但这有点疯狂。
try:
assert False, "A Message"
except AssertionError, e:
raise Exception( e.args )
为什么不是以下?这不那么疯狂。
if not someAssertion: raise Exception( "Some Message" )
它只是比assert
声明稍微冗长,但并没有违反我们对断言失败引发的期望AssertionError
。
考虑一下。
def myAssert( condition, action ):
if not condition: raise action
然后你可以或多或少地用这样的东西替换你现有的断言。
myAssert( {{ the original condition }}, MyException( {{ the original message }} ) )
完成此操作后,您现在可以自由地对启用或禁用或您想要做的任何事情大惊小怪。
另外,请阅读警告模块。这可能正是您想要做的。
这个怎么样?
>>> def myraise(e): raise e
...
>>> cond=False
>>> assert cond or myraise(RuntimeError)
Traceback (most recent call last):
File "", line 1, in
File "", line 1, in myraise
RuntimeError
永远不要将断言用于逻辑!仅用于可选的测试检查。请记住,如果 Python 在启用优化的情况下运行,则断言甚至不会编译到字节码中。如果你这样做,你显然关心引发的异常,如果你关心,那么你首先使用错误的断言。
使用选项运行时, Python 也会跳过if __debug__:
块。-o
以下代码更冗长,但无需 hack 即可满足您的需求:
def my_assert(condition, message=None):
if not condition:
raise MyAssertError(message)
if __debug__: my_assert(condition, message)
if __debug__:
您可以通过在内部移动条件来缩短它my_assert()
,但是当启用优化时它将被调用(内部没有任何操作)。
您可以让上下文管理器在 with 块内为您进行转换(其中可能包含多个断言,或更多代码和函数调用或您想要的。
from __future__ import with_statement
import contextlib
@contextlib.contextmanager
def myassert(exctype):
try:
yield
except AssertionError, exc:
raise exctype(*exc.args)
with myassert(ValueError):
assert 0, "Zero is bad for you"
请参阅此答案的先前版本以直接替换构造的异常对象(KeyError("bad key")
),而不是重用断言的参数。
至少在 Python 2.6.3 中,这也可以:
class MyAssertionError (Exception):
pass
AssertionError = MyAssertionError
assert False, "False"
Traceback (most recent call last):
File "assert.py", line 8, in <module>
assert False, "False"
__main__.MyAssertionError: False
如果你想为此使用断言,这似乎工作得很好:
>>> def raise_(e): raise e
...
>>> x = -2
>>> assert x >= 0, raise_(ValueError('oops'))
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 1, in raise_
ValueError: oops
请注意,在断言中,逗号后面的内容只有在条件为假时才会被评估,因此ValueError
只会在需要时创建和引发。
为了查看尝试是否有任何开销,我尝试了这个实验
这是 myassert.py
def myassert(e):
raise e
def f1(): #this is the control for the experiment
cond=True
def f2():
cond=True
try:
assert cond, "Message"
except AssertionError, e:
raise Exception(e.args)
def f3():
cond=True
assert cond or myassert(RuntimeError)
def f4():
cond=True
if __debug__:
raise(RuntimeError)
$ python -O -mtimeit -n100 -r1000 -s'import myassert' 'myassert.f1()'
100 loops, best of 1000: 0.42 usec per loop
$ python -O -mtimeit -n100 -r1000 -s'import myassert' 'myassert.f2()'
100 loops, best of 1000: 0.479 usec per loop
$ python -O -mtimeit -n100 -r1000 -s'import myassert' 'myassert.f3()'
100 loops, best of 1000: 0.42 usec per loop
$ python -O -mtimeit -n100 -r1000 -s'import myassert' 'myassert.f4()'
100 loops, best of 1000: 0.42 usec per loop