LoginSignup
3
7

More than 1 year has passed since last update.

【Python】最初、混同してしまう「assert, except, raise」文について

Last updated at Posted at 2022-06-16

assert

デバックを作業を容易に行うため に使用されます

条件式がFalseの場合にはAssertionErrorを発生させて停止。
Trueの場合はそのまま処理が続きます

sample.py
assert "条件式をここに書く", "Falseの場合のメッセージ"
sample.py
# 例)
# 1. AssertionErrorは発生せずに、処理は続く
assert "apple" == "apple", "りんごでは無いです"

# 2
assert "apple" == "orange", "りんごでは無いです"
# => AssertionError : りんごでは無いです

では、if文と、assert文の違いは?

同じ様に条件分岐をするif分との違いは何なのか

sample.py
if "apple" == "apple":
    is_same_fruit = True
else:
    is_same_fruit = False

if文はTrueでもFalseでも条件によって処理を行うのに対し、
assert文はTrueの場合は処理を行うが、Falseの場合は AsserttionErrorを発生させます

except

exceptは、例外処理を受け取ります。 また、キャッチするとも言います
想定外の処理が起きた時用の処理を用意します

try文と一緒に使用します

sample.py
try:
    エラーが発生する可能性がある処理
except:
    例外発生時の処理

こんな使い方がある

①未定義の変数の例外をキャッチする

未定義の変数は組み込み例外クラスのNameErrorが発生します

sample.py
try:
    print(apple)
except ValueError as e:
    print("ValueError をキャッチ")
    print(e)
except NameError as e:
    print(("NameErrorをキャッチ"))
    print(e)

# => NameErrorをキャッチ
# => name 'apple' is not defined

tryにエラーが発生する可能性がある処理を書いています
仮にエラーが発生した場合、exceptで例外をキャッチ出来るようになっています
上記の例では、未定義の変数が参照されようとしたので例外クラスであるNameErrorが発生し、except NameError as e:でキャッチしています

仮に、try/exceptを書かない場合

sample.py
print(apple)

# =>  print(apple)
# NameError: name 'apple' is not defined

キャッチするのではなく、例外であるNameErrorが発生して処理が終了します

hogehoge:「そもそも、なぜ例外処理をキャッチする意味がある?」
hogehoge:「エラーとして、スタックトレースに出力されるんなら同じ事では?」

理由については後ほど記載。

② 例外オブジェクトを取得する

①のサンプルコードにもありましたが、
except 例外クラス as 変数:とすることで、例外オブジェクトを取得することが出来ます

sample.py
try:
    print(apple)
except NameError as e:
    print(e)
    print(type(e))
except:
    print("それ以外の例外")

# => name 'apple' is not defined
# => <class 'NameError'>

exceptにおける疑問の解消

hogehoge:「そもそも、なぜ例外処理を発生させる意味がある?」
hogehoge:「エラーとして、スタックトレースに出力されるんなら同じ事では?」

  • 該当の処理だけを止めるため
    • 例外が起きると、処理が止まるが全体の処理が止まるわけではなく、例外の該当の箇所だけで収まる。
  • エラー原因の解析のため
    • エラーが発生する可能性のある箇所で、意図的に例外としてキャッチすることで、解析しやすいようにエラーメッセージを添えることが出来る
  • ソフトランディングさせるため
    • その後の処理で誤ったデータが登録されないように
    • DBをロールバックさせるため

hogehoge:「if elseでも代替えできるんじゃ?」

処理自体は可能。
だが、全てのエラーを予測して例外をキャッチしなければなりません
よって、try/except文を使います

raise

例外を意図的に発生させたい場合に使用します
例外が発生する可能性のある箇所に書きます
raiseで例外を発生させて、exceptでキャッチしたりします(後ほど、詳しく説明します)

sample.py
raise 例外クラスメッセージ

# 例
raise RuntimeError("Something bad happened")

エラーが発生するとこんな感じでスタックトレースに出ます

Traceback (most recent call last):
  File "<stdin>", line 4, in <module>
RuntimeError: Something bad happened

また、

  • 独自で例外クラスを作る
  • Exceptionクラスを継承したクラスを作る

なども可能です。

例えばこんな挙動になる

●<シンプルに、例外を発生させて、該当する例外をキャッチ>

raise NameErrorで例外を発生させて、例外が検出された時、
該当の例外クラスのexcept句が実行されます

sample.py
try:
    raise NameError("変数が定義されていません")
except NameError as e:
    print(e)

# => 変数が定義されていません

●<例外を発生させて、該当する例外とは異なるのでキャッチ出来ない>

NameErrorを発生させていますが、except句ではValueErrorを待っているのでキャッチ出来ません

except句内の出力が確認できないことが分かります

しかし、例外はスタックトレースへエラーメッセージと共に送出されています

sample.py
try:
    raise NameError("変数が定義されていません")
except ValueError as e:
    print(e)
    print("例外をキャッチ!")

# Traceback (most recent call last):
#   File "Main.py", line 5, in <module>
#     raise NameError("変数が定義されていません")
# NameError: 変数が定義されていません

hogehoge:「結局のところ一番知りたいのは、exceptとraiseの違いは細かな違いって何?」

except と raiseの違い

公式にも記載があります

例外が発生したかどうかを判定したいだけで、その例外を処理するつもりがなければ、単純な形式の raise 文を使って例外を再送出させることができます
https://docs.python.org/ja/3/tutorial/errors.html#raising-exceptions

つまりどういうことかというと、

  • 例外として、エラーメッセージを送出したいだけとかならraiseでOK
  • 例外が発生した後、処理を行いたい場合にexceptでキャッチする
    • エラーメッセージをdictへ整形したり、エラーログに残したり、csv出力したり とか

具体例を説明すると、

ここからは、コードをいくつか例を用いて説明します
(理解できている方は飛ばしてください)

●<raiseでエラーメッセージを送出したいだけ>

sample.py
raise NameError("変数が定義されていません")

# Traceback (most recent call last):
#   File "Main.py", line 1, in <module>
#     raise NameError("変数が定義されていません")
# NameError: 変数が定義されていません

●<例外を発生させて、except句内で処理する>

Exceptionクラスを継承している独自で作成したSampleExceptionを使って説明します。

条件分岐を行なって例外を発生させます

except句では、SampleExceptionをキャッチして、except句内の処理が行われます

sample.py
class SampleException(Exception):
    pass

def cal():
    try:
        a = 10
        if a % 4 == 0:
            raise SampleException("10は4で割り切れてしまいました")
        elif a % 2 == 0: # 当条件に当てはまり例外が発生
            raise SampleException("10は2で割り切れてしまいました")
        elif a % 5 == 0:
            raise SampleException("10は5で割り切れてしまいました")
    except SampleException as e:
        print(e)
        print("例外キャッチ")
        print("ここに処理を書く")

cal()

# 10は2で割り切れてしまいました
# 例外キャッチ
# ここに処理を書く

raiseを使用する場面とは?

  • 致命的なエラーが発生する可能性のある時
  • 自分が作った処理にエラーが発生した際に、それを外部に伝えるため

以下記事が、分かりやすいです

参考

3
7
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
7