Python核心技術と実戦——20|assertの合理的利用

10382 ワード

私たちは普段コードを見ているとき、多かれ少なかれassertの存在を見たことがあり、code reviewの中にはassertを増やすことでコードをより丈夫にすることもできます.しかしそれでもassertは無視されがちですが、この目立たない使い方は、適切に使えば、私たちのコードに役立ちます.そこで、今日はassertの使い方を見てみましょう.
assertとは?
Pythonのassertはdebugのツールと見なされ、主に条件が満たされているかどうかをテストし、テストの条件が満たされている場合は何も実行せず、pass文をかなり実行している.条件が一致しない場合、AssertionErrorが放出され、特定のエラー情報(optional)が返されます.彼の具体的な文法はこうです.
assert_stmt ::='assert' expression [',',Exception]

簡単な形式のassert expressionの例を見てみましょう.
assert 1 == 2

次の2行のコードに相当します.
if __debug__:
    if not expression  : raise AssertionError

別のフォーマットを見てみましょう
assert 1 == 2,'assertion is wrong'

次の2行のコードフォーマットに相当します
if __debut__:
    if not expression1:raise AssertionError(expression2)

ここの__debug__Pythonプログラムの実行時に-oというオプションが付いている場合は定数です.
Python -O test.py 

では、プログラム内のすべてのassert文が失効し、定数_debug__False、逆はTrueです.
ただし、定数に直接値を割り当てるのは違法です.彼の値は解釈器が動作するときに決定されているので、途中では変更できません.
また、assertを使うときは必ずカッコをつけないでください.例えば、次の形式です.
assert (1 == 2,'this should fail')

このように書くと、式が正しいかどうかにかかわらず、assertチェックはfailではなく、プログラムはSyntaxWarningのメッセージしか与えないので、正しい書き方はこうです
assert 1 ==2 ,'this should fail'
##########  ##########
AssertionError: this should fail

総じて言えば、asssertのプログラムにおける役割は、コードに対してinternalのself-checkをすることです.assertを使用すると、この条件が必ず発生するか、必ず発生しないかを確定していることを示します.
例を挙げると、例えば、ここに与えられたパラメータが人間の性別である必要がある関数がありますが、通常の性別の男性と女性は、assertを使用してプログラムの不正な入力を防ぐことができます.プログラムにバグがなければassertは永遠に異常を投げ出すことはありませんが、異常を投げ出すとプログラムがどこで問題を起こしたのか、位置決めが容易になります.
assertの使い方
assertの基本的な文法と概念を述べた後、いくつかの実際のシーンを通じてPythonでのassertの使い方を見て、assertの使用シーンを明らかにします.
最初の例.タイムアウトして販促活動をし、一部の商品を割引する準備をしていると仮定すると、バックグラウンドにはapplyが必要です.discount()の関数は、元の価格と割引を入力し、出力は割引後の価格であることを要求します.では、私たちは大体次のように書くことができます.
def apply_discount(price,discount):
    update_price = price*(1-discount)
    assert 0 < update_price'price should be greated or equal to 0'
    return update_price

いくつかのグループの数を指定することで、彼の機能を検証することができます.
print(apply_discount(100,0.8))
##########  ##########
19.999999999999996


print(apply_discount(100,2))
##########  ##########
AssertionError: price should be greated or equal to 0

discountが0.2の場合、出力は正常ですが、discountが2になった場合、プログラムは異常を投げ出すことがわかります.このとき,開発者が関連コードを修正したり,新しい機能を追加したりした場合,テストを実行する際に問題が容易に見られる.したがって,assertの加入は,バグの発生を効果的に予防し,プログラムの頑丈性を高めることができる.
第2の例では、最も一般的な除算操作はどの分野でも遭遇します.例えば、貨物の販売の平均価格を知りたい場合は、販売総額と販売数を与えなければなりません.そうすれば、平均価格が計算されます.
def calculate_average_price(totle_sales,num_sales):
    assert num_sales > 0,'number of sales should be greater than 0'
    return totle_sales/num_sales

同様に、関数にassert文を追加し、販売の合計数を0より大きくしなければならないことを規定し、バックグラウンド計算のエラーを防止することができます.
上記の2つの例に加えて、実際の作業では、assertには次のような一般的な使い方があります.
def fun(input):
    assert isinstance(input,list),'input must be type of list'
    if len(input) == 1:
        pass
    elif len(input) == 2:
        pass
    else:
        pass

関数の中の操作はinputがlistであることを前提にして、私たちの関数が始まるところにassert検査を加えて、プログラムの間違いを防止します.
しかし、この関数にassertを追加する前提は、プログラムの入力がリストであり、他のデータ型ではないことを十分に確定することです.もし私たちのこの関数が多態で、異なるデータ型に対して異なる操作があるならば、ifと書くべきです...else...の条件文です
def fun(input):
    if isinstance(input,int):
        pass
    elif isinstance(input,str):
        pass
    else:
        pass

assertのエラー例
前に述べたassertの使用シーンを通じて、私たちを困惑させる可能性があります.多くの場所でassertを使用することができます.では、ifがたくさんあります.elseの条件文もassertに変えることができますか?この考えは正確ではないかもしれない.次に、いくつかの典型的な誤った使い方を見てみましょう.
例えば、いくつかのデータを削除しますが、削除操作はadminユーザーでなければなりません.次のコードがあります.
def delete_data(user,data_id):
    assert user_is_admin(user),'user must  admin'
    assert data_exist(data_id),'data must  exist'
    delete(data_id)

では、上のコードに何か問題がありますか?
assertのチェックはオフにできますが、Pythonプログラムを実行するときに、-Oオプションを追加するとassertが無効になります.したがって、assertのチェックがオフになるとuser_is_admin()とcourse_exist()の2つの関数は実行されず、次の問題が発生します.
1.どのユーザーでもデータを削除できます.
2.データが存在するかどうかにかかわらず、壁紙で削除操作を行う
これにより、プログラムに大きなセキュリティ・ホールがもたらされます.正しい方法は、条件文を使用して対応するチェックを行い、関連する異常情報を投げ出すことです.
def delete_data(user,data_id):
    if not user_is_admin(user):
        raise Exception('user must be admin')
    if not data_exist(data_id):
        raise Exception('data must exist')
    delete(data_id)

もう一つ例を見てみると、ファイルを開いたり、データの処理、読み取りなどの一連の操作をしたりしたいのですが、次の書き方もリスクがあります.
def read_and_process(path):
    assert file_exist(path),'file must exist'
    with opne(path) as f:
        pass

assertの使用により、強制指定ファイルが存在しなければならないことを示しているが、実際にはこの仮定は成立せず、また、ファイルを開く操作が他の異常をトリガーする可能性もある.正しいやり方はtryで...Exceptで解決
def read_and_process(path):
    try:
        with open(path) as f:
            pass
    except Exception as e:
        pass

総じて言えば、assertはrun-time errorの検査に実際に使用されない.例えば、ファイルを開こうとしたが、ファイルは存在しない.あるいはネットから1つのファイルをダウンロードしようとしたが、途中でネットが切れたなど.これらの場合、エラーと例外処理が一般的に使用されます.
まとめ
私たちは今日assertの使い方を学びました.assertは通常、コードに必要なself-checkを行い、コードを書くときにこのような状況が必ず発生するか、必ず発生しないことを示しています.注意しなければならないのは、assertを使用するときは、必ずカッコをつけてはいけません.そうしないと、式が正しいかどうかにかかわらず、assertのチェックはfailになりません.また,プログラム中のassertは,−Oなどのオプションでグローバルdisableされる.
いくつかのシーンの応用を通じて、assertの合理的な使用はコードの丈夫さを増加させることができて、同時にプログラムの間違いの時に開発者の位置付けの調査を便利にすることができます.
しかし、私たちもassertの使用に注意しなければなりません.多くの場合、プログラムに現れる異なる状況は予想通りです.西洋薬は異なる案で処理します.この時、条件文はもっと適切ですが、プログラムのrun-time errorの中には、異常処理がもっと適切です.