アサートとは何ですか?
Pythonのassertステートメントは、主に条件が満たされているかどうかをテストするために使用される、優れたデバッグツールであると言えます。テスト条件が満たされた場合、passステートメントの実行と同等の処理は行われません。テスト条件が満たされない場合、例外AssertionErrorがスローされ、特定のエラー情報(オプション)が返されます。
具体的な構文は次のとおりです。
assert_stmt ::= "assert" expression ["," expression]
最初に、次の例のような単純な形式のアサート式を見てみましょう。
assert 1 == 2
これは、次の2行のコードに相当します。
if __debug__:
if not expression: raise AssertionError
次の例のように、assert expression1、expression2の形式を見てみましょう。
assert 1 == 2, 'assertion is wrong'
これは、次の2行のコードに相当します。
if __debug__:
if not expression1: raise AssertionError(expression2)
ここで、__ debug__は定数です。PythonプログラムがPythontest.py -Oなどの-Oオプションを指定して実行された場合、プログラム内のすべてのアサートステートメントは無効になり、定数__debug__はFalseになります。それ以外の場合、__ debug__はTrueになります。
ただし、定数__debug__に値を直接割り当てることは違法であることに注意してください。その値は、インタープリターの実行開始時に決定され、途中で変更できないためです。
さらに、次の例のように、assertを使用するときは括弧を追加しないように注意してください。
assert(1 == 2, 'This should fail')
# 输出
<ipython-input-8-2c057bd7fe24>:1: SyntaxWarning: assertion is always true, perhaps remove parentheses?
assert(1 == 2, 'This should fail')
このように記述すると、式が正しいか間違っているかに関係なく(たとえば、ここでは1 == 2が明らかに間違っている)、アサートチェックが失敗することはなく、プログラムはSyntaxWarningのみを提供します。
正しい書き方は、括弧なしで次の書き方をする必要があります。
assert 1 == 2, 'This should fail'
# 输出
AssertionError: This should fail
一般に、プログラムでのassertの役割は、コードの内部セルフチェックを行うことです。アサートを使用してください、あなたはかなり確信しています。この状態が発生するか、発生することはありません。
たとえば、関数がある場合、パラメータの1つは人の性別です。性別は男性と女性のみであるため(ここでは生物学的性別のみを指します)、assertを使用してプログラムの不正入力を防ぐことができます。プログラムにバグがない場合、assertは例外をスローしません。例外をスローすると、プログラムに問題があることがわかり、エラーメッセージに基づいてエラーの原因を簡単に見つけることができます。
assertの使用法
assertの基本的な構文と概念について説明した後、いくつかの実用的なアプリケーション例を通じてPythonでのassertの使用法を見て、assertの使用シナリオを理解しましょう。
最初の例では、現在オタク時間を使用して列のプロモーションアクティビティを実行し、一部の列を割引する準備をしていると仮定します。そのため、バックグラウンドでapply_discount()関数を記述し、入力を元の価格と割引にする必要があります。出力は割引価格です。次に、大まかに次のように書くことができます。
def apply_discount(price, discount):
updated_price = price * (1 - discount)
assert 0 <= updated_price <= price, 'price should be greater or equal to 0 and less or equal to original price'
return updated_price
ご覧のとおり、新しい価格を計算した後、割引価格を確認するためのアサートステートメントも記述しました。この値は0以上で元の価格以下である必要があります。そうでない場合、例外がスローされます。
この機能を検証するために、いくつかの数字のセットを入力してみることができます。
apply_discount(100, 0.2)
80.0
apply_discount(100, 2)
AssertionError: price should be greater or equal to 0 and less or equal to original price
もちろん、割引が0.2の場合、出力は80であり、問題ありません。ただし、割引が2の場合、プログラムは次の例外をスローします。
AssertionError:price should be greater or equal to 0 and less or equal to original price
このように、開発者が関連するコードを変更したり、新しい関数を追加したりして割引値が異常になった場合、テストを実行するときに問題を簡単に見つけることができます。冒頭で述べたように、assertを追加すると、バグの発生を効果的に防ぎ、プログラムの堅牢性を向上させることができます。
別の例、最も一般的な除算演算を見てみましょう。これは、あらゆる計算分野でよく見られます。オタクタイムも例として取り上げます。オタクタイムのバックグラウンドで各列の平均販売価格を知りたい場合は、平均販売価格を簡単に計算できるように、総売上と販売数を指定する必要があります。
def calculate_average_price(total_sales, num_sales):
assert num_sales > 0, 'number of sales should be greater than 0'
return total_sales / num_sales
同様に、バックグラウンドがまだ販売されていない列の価格を計算しないように、販売数は0より大きくなければならないことを規定するassertステートメントも追加しました。
これらの2つの例に加えて、実際の作業では、assertには次のシナリオなどの非常に一般的な用途がいくつかあります。
def func(input):
assert isinstance(input, list), 'input must be type of list'
# 下面的操作都是基于前提:input必须是list
if len(input) == 1:
...
elif len(input) == 2:
...
else:
...
ここで、関数func()のすべての操作は、入力がリストでなければならないという前提に基づいています。おなじみの要件ですか?次に、プログラムエラーを防ぐために、最初にアサートチェックを追加する必要があります。
もちろん、特定の状況に基づいて特定の分析を行う必要があります。たとえば、上記の例では、入力が他のデータ型ではなくリストである必要があると確信しているため、assertを追加できます。
プログラムで入力を他のデータ型にすることができ、データ型ごとに異なる処理方法がある場合は、ifelse条件ステートメントを作成する必要があります。
def func(input):
if isinstance(input, list):
...
else:
...
エラーの例をアサート
先ほど、assertの非常に多くの使用シナリオについて説明しました。これは、幻想を与えたり、少し混乱させたりする可能性があります。assertはさまざまな場所で使用できるので、条件ステートメントをassertに置き換えることができますか?そう考えるのは正確ではありません。次に、いくつかの典型的な間違った使用法を見て、当然のことと思われるいくつかの使用法を避けましょう。
オタクタイムを例にとると、次のシナリオを想定しています。バックグラウンドで長期間オンラインになっている一部の列を削除する必要がある場合があるため、関連する開発者は次の列削除機能を設計しました。
def delete_course(user, course_id):
assert user_is_admin(user), 'user must be admin'
assert course_exist(course_id), 'course id must exist'
delete(course_id)
Geek Timeでは、列を削除するには管理者である必要があり、列コースが存在する必要があると規定されています。一部の学生は、要件に非常に精通していることに気付いたため、対応するアサートチェックを前に追加しました。それで、あなたにそれについて考えてもらいたいのですが、このように書くのは正しいですか?
答えは明らかにノーです。コード関数の観点からはこれは正しいと思うかもしれません。しかし、実際のエンジニアリングでは、基本的に誰もそのように書くことはありません。どうして?
前に述べたように、アサートチェックをオフにできることに注意してください。たとえば、Pythonプログラムを実行しているときに、-Oオプションを追加するとアサートが無効になります。したがって、アサートチェックをオフにすると、user_is_admin()とcourse_exist()の2つの関数は実行されません。これにより、次のことが可能になります。
- すべてのユーザーは、列コースを削除する権利があります。
- また、コースの有無に関係なく、強制的に削除することができます。
これは明らかにプログラムに大きなセキュリティホールをもたらします。したがって、正しいアプローチは、条件ステートメントを使用して対応するチェックを実行し、合理的に例外をスローすることです。
def delete_course(user, course_id):
if not user_is_admin(user):
raise Exception('user must be admin')
if not course_exist(course_id):
raise Exception('coursde id must exist')
delete(course_id)
別の例を見てみましょう。ファイルを開いて、データの読み取りや処理などの一連の操作を実行する場合、次の書き込みは明らかに正しくありません。
def read_and_process(path):
assert file_exist(path), 'file must exist'
with open(path) as f:
...
assertを使用しているため、ファイルが存在する必要があることを強制的に指定していることを示していますが、実際には、多くの場合、この仮定は当てはまりません。さらに、ファイルを開く操作によって他の例外がトリガーされる場合もあります。したがって、正しいアプローチは、tryとexceptを使用して例外を処理し、以下を解決することです。
def read_and_process(path):
try:
with open(path) as f:
...
except Exception as e:
...
一般に、assertは実行時のエラーチェックには適用されません。