web-dev-qa-db-ja.com

Pythonの「アサート」に選択した例外をスローさせる

assertの代わりに、選択した例外をAssertionErrorにスローさせることはできますか?

更新:

私の動機を説明します。これまで、私は独自の例外を発生させるアサーションスタイルのテストを行ってきました。たとえば、特定の引数を使用してNodeオブジェクトを作成すると、その引数がノードの作成に適しているかどうかが確認され、そうでない場合はNodeErrorが発生します。

ただし、Pythonには-oアサートがスキップされるモード。これは、プログラムを高速化するために使用可能にしたいと考えています。しかし、私は自分自身の例外を持ちたいと思っています。それが私が自分の例外でアサートを使いたい理由です。

42
Ram Rachum

これは動作します。しかし、それはちょっとおかしいです。

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 }} ) )

これを実行すると、有効化または無効化など、何をしようとしているかを自由に試すことができます。

また、 warnings モジュールも参照してください。これはまさにあなたがやろうとしていることかもしれません。

54
S.Lott

これはどう?


>>> 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

24
John La Rooy

ロジックにアサーションを使用しないでください!オプションのテストチェックのみ。 Pythonが最適化をオンにして実行されている場合、アサートはバイトコードにコンパイルされることすらありません。これを実行している場合は、例外が発生することを明らかに気にします。それからあなたはそもそも間違ったアサートを使っています。

8
ironfroggy

また、Pythonは_if __debug__:_オプションを指定して実行すると_-o_ブロックをスキップします。次のコードはより冗長ですが、ハックすることなく必要なことを実行します。

_def my_assert(condition, message=None):
    if not condition:
        raise MyAssertError(message)

if __debug__: my_assert(condition, message)
_

_if __debug__:_条件をmy_assert()内に移動することで短くすることができますが、最適化が有効になっている場合は(内部でアクションなしで)呼び出されます。

8
Denis Otkidach

コンテキストマネージャー で、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"))には、この回答の以前のバージョンを参照してください。

4
u0b34a0f6ae

試行にオーバーヘッドがあるかどうかを確認するために、この実験を試しました

ここに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
3
John La Rooy

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
3
Ber

これにアサートを使用したい場合、これはかなりうまくいくようです:

>>> 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

アサートでは、コンマの後のものは条件がfalseの場合にのみ評価されるため、ValueErrorは必要なときにのみ作成および生成されることに注意してください。

2
uryga