web-dev-qa-db-ja.com

Python assertのベストプラクティス

  1. デバッグ目的で使用するのではなく、標準コードの一部としてassertを使用することによるパフォーマンス上またはコード保守上の問題はありますか?

    です

    assert x >= 0, 'x is less than zero'
    

    よりも良くも悪くも

    if x < 0:
        raise Exception, 'x is less than zero'
    
  2. また、if x < 0 raise errorのように常にチェックされるtry/except/finallyのようなビジネスルールを設定する方法はあります。そのため、コードのいつでもxが0より小さい場合は、関数の開始時にassert x < 0を設定する場合のようにエラーが発生します。 xが0未満になる関数内のどこかで例外が発生しますか?

433
meade

関数全体でxがゼロより小さくなったときに自動的にエラーをスローできるようにします。あなたは クラス記述子 を使うことができます。これが一例です。

class LessThanZeroException(Exception):
    pass

class variable(object):
    def __init__(self, value=0):
        self.__x = value

    def __set__(self, obj, value):
        if value < 0:
            raise LessThanZeroException('x is less than zero')

        self.__x  = value

    def __get__(self, obj, objType):
        return self.__x

class MyClass(object):
    x = variable()

>>> m = MyClass()
>>> m.x = 10
>>> m.x -= 20
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "my.py", line 7, in __set__
    raise LessThanZeroException('x is less than zero')
LessThanZeroException: x is less than zero
139
Nadia Alramli

アサートは、が決して起こらないという条件をテストするために使用されるべきです。目的は、プログラム状態が破損した場合に早期にクラッシュすることです。

おそらく例外が発生する可能性があるエラーに使用されるべきです、そしてあなたはほとんど常にあなた自身の例外クラスを作成するべきです


たとえば、設定ファイルからdictに読み込む関数を作成している場合は、ファイル内の形式が不適切であればConfigurationSyntaxErrorが発生するはずですが、assertではNoneを返さないことになります。


あなたの例では、xがユーザーインターフェースまたは外部ソースから設定された値である場合、例外が最善です。

xが同じプログラム内の自分のコードによってのみ設定される場合は、アサーションを使用してください。

692
Deestan

"assert"ステートメントは、コンパイルが最適化されると削除されます。つまり、パフォーマンスと機能の両方に違いがあります。

現在のコードジェネレータは、コンパイル時に最適化が要求されたときにassert文のコードを発行しません。 - Python 2.6.4ドキュメント

アプリケーションの機能を実装するためにassertを使用してから、本番環境へのデプロイメントを最適化すると、 "but-it-works-in-dev"の欠陥に悩まされることになります。

PYTHONOPTIMIZE および - O -OO を参照してください。

336
John Mee

assertname__の4つの目的

Alice、Bernd、Carl、Daphneの4人の同僚と20万行のコードを処理するとします。彼らはあなたのコードを呼び、あなたは彼らのコードを呼びます。

assertname__には4つの役割があります

  1. Alice、Bernd、Carl、Daphneにあなたのコードが期待していることを伝えてください。
    タプルのリストを処理するメソッドがあり、それらのタプルが不変でないとプログラムロジックが壊れる可能性があるとします。

    def mymethod(listOfTuples):
        assert(all(type(tp)==Tuple for tp in listOfTuples))
    

    これは、ドキュメント内の同等の情報よりも信頼性が高く、保守がはるかに簡単です。

  2. あなたのコードが期待していることをコンピュータに知らせます。
    assertname__はあなたのコードの呼び出し側から適切な振る舞いを強制します。コードがAlicesのコードを呼び出し、Berndのコードがyourのコードを呼び出す場合、assertname__を指定せずに、プログラムがAlicesコードでクラッシュした場合、BerndはそれをAliceの障害と見なし、Aliceを調べ、それをあなたの障害と見なします。彼の事実たくさんの仕事が失われました。
    アサーションを使うと、電話を間​​違えた人は誰でも、自分のせいではなく自分のせいであることがすぐにわかります。アリス、ベルント、そしてあなたは皆恩恵を受けます。膨大な時間を節約します。

  3. 自分のコードがある時点で達成したことを自分自身のコードも含めて読者に知らせます。
    あなたがエントリーのリストを持っていて、それらのそれぞれがきれいであることができる(これは良いです)、またはそれがより小さくても、trale、gullup、またはtwinkledであることもできます(これらはすべて受け入れられません)。それが小さければ、それは緩和されなければならない。それが裏切りであるならば、それは賛美されなければなりません。もしそれが暴走するならば、それは速歩されなければならない(そしてそれからおそらくペーシングもされる)。きらめくのであれば、木曜日以外はまたきらめく必要があります。あなたはアイデアを得ます:それは複雑なものです。しかし最終的な結果は、すべてのエントリがクリーンであるということです(またはそうあるべきです)。やるべき正しいこと(TM)は、次のようにあなたのクリーニングループの効果をまとめることです。

    assert(all(entry.isClean() for entry in mylist))
    

    このステートメントは、正確にという素晴らしいループが達成されているということを理解しようとする人にとって頭痛の種を省きます。そして、これらの人々の最も頻繁なのは、おそらくあなた自身でしょう。

  4. ある時点でコードが達成したことをコンピュータに知らせます。
    速歩の後にそれを必要とするエントリのペースを忘れた場合、assertname__はあなたの日を節約し、あなたのコードがDaphneのずっと後の方で壊れるのを避けるでしょう。

私の考えでは、assertname __の2つの目的(1と3)と保護(2と4)は同じように価値があります。
assertname__がキャッチしようとする間違い(ケース1の場合)を防ぐことができるので、人々に知らせることは、コンピュータに知らせることよりもmore貴重なことがありますいずれにせよその後の間違いの。

116
Lutz Prechelt

他の答えに加えて、assert自身が例外をスローしますが、AssertionErrorsのみです。実用的な観点からは、どの例外をキャッチするかをきめ細かく制御する必要がある場合には、アサーションは適していません。

21
outis

このアプローチで本当に間違っている唯一のことは、assertステートメントを使って非常に説明的な例外を作るのが難しいということです。もっと単純な構文を探しているのなら、でもでもこのようなことができることを忘れないでください。

class XLessThanZeroException(Exception):
    pass

def CheckX(x):
    if x < 0:
        raise XLessThanZeroException()

def foo(x):
    CheckX(x)
    #do stuff here

もう1つの問題は、通常の条件チェックにassertを使用すると、-Oフラグを使用してデバッグassertを無効にするのが困難になることです。

18
Jason Baker

前述したように、アサーションはあなたのコードがある時点に到達するべきではないときに使用されるべきです。おそらく私がアサーションを使用するのを見ることができる最も有用な理由は不変の/ pre/postconditionです。これらは、ループまたは関数の各繰り返しの開始時または終了時に当てはまる必要があります。

例えば、再帰的関数(2つの別々の関数なので、1つは悪い入力を扱い、もう1つは悪いコードを扱います。再帰と区別するのは難しいです)私がif文を書くのを忘れた場合、これは明らかになります、何が間違っていました。

def SumToN(n):
    if n <= 0:
        raise ValueError, "N must be greater than or equal to 0"
    else:
        return RecursiveSum(n)

def RecursiveSum(n):
    #precondition: n >= 0
    assert(n >= 0)
    if n == 0:
        return 0
    return RecursiveSum(n - 1) + n
    #postcondition: returned sum of 1 to n

これらのループ不変式はしばしばアサーションで表すことができます。

7
matts1

ここでの英語の単語assertは、の意味で使用されています肯定avow「チェック」またはが「」であるべきという意味ではありません。これは、あなたがコーダーとして宣言文を作成していることを意味します。

# I solemnly swear that here I will tell the truth, the whole truth, 
# and nothing but the truth, under pains and penalties of perjury, so help me FSM
assert answer == 42

コードが正しければ、 シングルイベントの混乱 、ハードウェア障害などno assertは失敗しますそのため、エンドユーザーに対するプログラムの動作に影響を与えてはいけません。特に、例外的なプログラム条件の下でも、assertは失敗することはありません。それは決して起こりません。それが起こるならば、プログラマーはそれのためにザッピングするべきです。

6
Antti Haapala

パフォーマンスの問題はありますか?

  • 「速く動作させる前に最初に動作させる」を忘れないでください。
    どんなプログラムでも、その速度に関係するのはごくわずかです。パフォーマンスの問題であることが証明されていれば、assertを常に追い出したり単純化したりすることができます - そしてそれらのほとんどは決してそうしないでしょう。

  • 実用的であること
    空ではないタプルのリストを処理するメソッドがあり、それらのタプルが不変でない場合はプログラムロジックが壊れるとします。あなたが書く必要があります:

    def mymethod(listOfTuples):
        assert(all(type(tp)==Tuple for tp in listOfTuples))
    

    リストのエントリ数が10エントリになる傾向がある場合、これはおそらく問題ありませんが、エントリ数が100万エントリになると問題になる可能性があります。しかし、この貴重なチェックを完全に破棄するのではなく、単に次のようにダウングレードすることができます。

    def mymethod(listOfTuples):
        assert(type(listOfTuples[0])==Tuple)  # in fact _all_ must be tuples!
    

    これは安価ですが、とにかく実際のプログラムエラーのほとんどを捕捉するでしょう。

3
Lutz Prechelt

アサートはチェックすることです -
1。有効な条件
2。有効なステートメント
3。真の論理
ソースコードの
プロジェクト全体を失敗させる代わりに、ソースファイルには適切ではないという警告が表示されます。

例1では、変数 'str'はNULLではありません。そのため、アサートや例外は発生しません。

例1:

#!/usr/bin/python

str = 'hello Pyhton!'
strNull = 'string is Null'

if __debug__:
    if not str: raise AssertionError(strNull)
print str

if __debug__:
    print 'FileName '.ljust(30,'.'),(__name__)
    print 'FilePath '.ljust(30,'.'),(__file__)


------------------------------------------------------

Output:
hello Pyhton!
FileName ..................... hello
FilePath ..................... C:/Python\hello.py

例2では、​​var 'str'はNULLです。そのため、assertステートメントを使用して、ユーザーが問題のあるプログラムより先に進むのを防ぐことができます。

例2:

#!/usr/bin/python

str = ''
strNull = 'NULL String'

if __debug__:
    if not str: raise AssertionError(strNull)
print str

if __debug__:
    print 'FileName '.ljust(30,'.'),(__name__)
    print 'FilePath '.ljust(30,'.'),(__file__)


------------------------------------------------------

Output:
AssertionError: NULL String

デバッグしたくない瞬間にソースコードのアサーション問題を認識しました。最適化フラグを無効にする

python -O assertStatement.py
何も印刷されません

1
akD

Java用のJBoss Drools というフレームワークがあり、ランタイム監視を行ってビジネスルールをアサートします。これは、質問の後半部分に答えます。しかし、私はそのようなPython用のフレームワークがあるのか​​どうか不明です。

PTVS、PyCharmなどのIDEでは、Wingのassert isinstance()ステートメントを使用して、不明なオブジェクトのコード補完を有効にすることができます。

0
denfromufa