30

私はしばしば python の assert ステートメントを使用してユーザー入力をチェックし、破損状態にある場合はフェイルファストします。-o(最適化された)フラグを使用してPythonを実行すると、アサートが削除されることを認識しています。個人的には、最適化モードでアプリを実行することはありませんが、念のため assert を避けるべきだと感じています。

書いたほうがすっきりした感じ

assert filename.endswith('.jpg')

よりも

if not filename.endswith('.jpg'):
    raise RuntimeError

これは assert の有効な使用例ですか? assertそうでない場合、Pythonのステートメントの有効なユースケースは何ですか?

4

8 に答える 8

26

アサーションは、不変条件または前提条件を表現するために使用する必要があります。
あなたの例では、予期しない入力をチェックするためにそれらを使用しています-そしてそれは完全に異なるクラスの例外です。

要件によっては、間違った入力で例外を発生させ、アプリケーションを停止してもまったく問題がない場合があります。ただし、コードは常に表現力に合わせて調整する必要があり、を上げることAssertionErrorはそれほど明確ではありません。
独自の例外を発生させるか、またはを発生させる方がはるかに優れていますValueError

于 2010-01-26T20:23:51.417 に答える
18

優雅であることは不可能なら、劇的であること

コードの正しいバージョンは次のとおりです。

if filename.endswith('.jpg'):
    # convert it to the PNG we want
    try:
        filename = convert_jpg_to_png_tmpfile(filename)
    except PNGCreateError:
        # Tell the user their jpg was crap
        print "Your jpg was crap!"

これは有効なケースです。IMHO は次の場合です。

  1. エラーは完全に、100%致命的であり、それを処理することは理解するには厳しすぎるでしょう。
  2. アサートは、何かによって論理法則が変化した場合にのみ失敗する必要があります

それ以外の場合は、不測の事態に対処してください。

ASSERT == "これ現実にはあり得ないことであり、発生した場合はあきらめます"

もちろん、これは同じではありません

#control should never get here

しかし、私はいつもそうします

#control should never get here
#but i'm not 100% putting my money where my mouth
#is
assert(False)

そうすれば、いいエラーが発生します。あなたの例では、ifバージョンを使用してファイルをjpgに変換します!

于 2010-01-26T20:03:57.007 に答える
9

完全に有効です。アサーションは、プログラムの状態に関する正式な主張です。

証明できないものに使用する必要があります。ただし、ロジックのチェックとして証明できると思われるものには便利です。

もう一つの例。

def fastExp( a, b ):
    assert isinstance(b,(int,long)), "This algorithm raises to an integer power"
    etc.

さらに別の。最後の主張は、証明可能であるべきなので、少しばかげています。

# Not provable, essential.
assert len(someList) > 0, "Can't work with an empty list."
x = someList[]
while len(x) != 0:
    startingSize= len(x)
    ... some processing ...
    # Provable.  May be Redundant.
    assert len(x) < startingSize, "Design Flaw of the worst kind."

さらに別の。

def sqrt( x ):
    # This may not be provable and is essential.
    assert x >= 0, "Can't cope with non-positive numbers"
    ...
    # This is provable and may be redundant.
    assert abs( n*n - x ) < 0.00001 
    return n

正式な主張を行う理由はたくさんあります。

于 2010-01-26T20:26:59.917 に答える
6

assertを使用せずに確実に実行する場合に、テスト中にアクティブにする必要があるコードに最適です。-o

個人的には決して実行しないかもしれませんが-o、コードがより大きなシステムになり、管理者がそれを実行したい場合はどうなり-oますか?

システムは正常に動作しているように見える可能性がありますが、を実行することでスイッチがオンになっている微妙なバグがあります-o

于 2010-01-26T20:23:26.353 に答える
4

個人的にはassert、予期しないエラー、または実際の使用では発生しないと予想されるものに使用します。ユーザーまたはファイルからの入力を処理するときは常に例外を使用する必要があります。これは、例外をキャッチして、ユーザーに「ねえ、.jpg ファイルを期待していた!!」と伝えることができるためです。

于 2010-01-26T20:06:16.063 に答える
1

S.Lott の回答が最適です。しかし、これは彼のコメントに追加するには長すぎるので、ここに入れました. とにかく、それは私が assert についてどのように考えているかということです。

とにかく、入力チェックには 2 つの考え方があります。ターゲットで実行することも、ソースで実行することもできます。

ターゲットでそれを行うことは、コード内にあります。

def sqrt(x):
    if x<0:
        raise ValueError, 'sqrt requires positive numbers'
    root = <do stuff>
    return root

def some_func(x):
    y = float(raw_input('Type a number:'))
    try:
        print 'Square root is %f'%sqrt(y)
    except ValueError:
        # User did not type valid input
        print '%f must be a positive number!'%y

さて、これには多くの利点があります。おそらく、sqrt を書いた人は、自分のアルゴリズムの有効な値について最もよく知っています。上記では、ユーザーから取得した値が有効かどうかわかりません。誰かがそれをチェックする必要があり、何が有効かを最もよく知っているコード、つまり sqrt アルゴリズム自体でそれを行うのは理にかなっています。

ただし、パフォーマンスが低下します。次のようなコードを想像してください。

def sqrt(x):
    if x<=0:
        raise ValueError, 'sqrt requires positive numbers'
    root = <do stuff>
    return root

def some_func(maxx=100000):
    all_sqrts = [sqrt(x) for x in range(maxx)]
    i = sqrt(-1.0)
    return(all_sqrts)

さて、この関数は sqrt を 100k 回呼び出します。そして毎回、sqrt は値が >= 0 であるかどうかを確認します。しかし、これらの数値を生成する方法により、それが有効であることは既にわかっています。これらの余分な有効なチェックは、実行時間を浪費するだけです。それらを取り除くのはいいことではありませんか?そして、ValueError をスローするものがあるので、それをキャッチして、間違いを犯したことに気付きます。私はサブ関数に依存してチェックするプログラムを書いているので、それがうまくいかないときの回復を心配するだけです。

2 番目の考え方は、ターゲット関数が入力をチェックする代わりに、定義に制約を追加し、呼び出し元が有効なデータで呼び出していることを確認する必要があるというものです。この関数は、適切なデータがあれば、そのコントラクトが示す内容を返すことを約束します。これにより、これらのチェックがすべて回避されます。呼び出し元は、送信元の関数よりも、送信しているデータ、データの送信元、固有の制約について多くのことを知っているためです。これらの最終結果は、コード コントラクトと同様の構造ですが、もともとこれは慣習によるものでした。つまり、以下のコメントのように設計されていました。

# This function valid if x > 0
def sqrt(x):
    root = <do stuff>
    return root

def long_function(maxx=100000):
    # This is a valid function call - every x i pass to sqrt is valid
    sqrtlist1 = [sqrt(x) for x in range(maxx)]
    # This one is a program error - calling function with incorrect arguments
    # But one that can't be statically determined
    # It will throw an exception somewhere in the sqrt code above
    i = sqrt(-1.0)

もちろん、バグが発生し、契約違反が発生する可能性があります。しかし、これまでのところ、結果はほぼ同じです。どちらの場合も、sqrt(-1.0) を呼び出すと、sqrt コード自体の内部で例外が発生し、例外スタックを調べて、バグがどこにあるかを突き止めることができます。

ただし、もっと陰湿なケースがあります...たとえば、私のコードがリスト インデックスを生成し、それを格納し、後でリスト インデックスを検索し、値を抽出し、何らかの処理を行うとします。そして、たまたま -1 のリスト インデックスを取得したとしましょう。これらのすべてのステップは実際にはエラーなしで完了する可能性がありますが、テストの最後に間違ったデータがあり、その理由はわかりません。

では、なぜ主張するのでしょうか。コントラクトをテストおよび証明している間に、障害に近いデバッグ情報を取得できるものがあると便利です。これは最初の形式とほぼ同じです。結局のところ、まったく同じ比較を行っていますが、構文的にはより簡潔で、コントラクトの検証に少し特化しています。副次的な利点は、プログラムが機能することを合理的に確信し、最適化して、より高いパフォーマンスとデバッグ可能性を探している場合、これらの冗長なチェックをすべて削除できることです。

于 2013-10-04T16:00:12.873 に答える