例外処理

例外処理とは

例外は「戻り値とは異なる形でメソッドからエラーを返す仕組み」である。例外処理(try-except)を利用すると、処理の途中で例外(エラー)が発生したときに、その処理を中断して別の処理に切り替えることによって発生した例外(エラー)にうまく対処できるようになる。

例えば、Pythonで「1/0」を実行すると組み込み例外「ZeroDivisionError」が発生し、例外が発生した箇所で処理が中断する。

num = 1 / 0
print("この部分は処理されない")


# Traceback (most recent call last):
#   File "C:\Users\yuki\main.py", line 1, in <module>
#     num = 1 / 0
#           ~~^~~
# ZeroDivisionError: division by zero

try-except文を用いると、例外が発生した箇所で処理を一時中断し、except節から処理を再開する。

try:
    num = 1 / 0
    print("この部分は処理されない")
except:
    print("例外が発生しました。")


# 例外が発生しました。

exceptの後に例外名を指定すると、例外の種類ごとに処理を振り分けることができ、asの後に設定した変数でエラーメッセージが受け取れる。

try:
    num = 1 / 0
    print("この部分は処理されない")
except ZeroDivisionError as e:
    print("ZeroDivisionErrorが発生しました。")
    print(f"Error: {e}")
except TypeError as e:
    print("TypeErrorが発生しました。")
    print(f"Error: {e}")
except ValueError as e:
    print("ValueErrorが発生しました。")
    print(f"Error: {e}")


# ZeroDivisionErrorが発生しました。
# Error: division by zero

さらに、elseを使うとエラーが発生しなかった場合に実行される処理を、finallyを使うと最後に必ず実行される処理を記述できる。

try:
    num = 1 / 2
except ZeroDivisionError as e:
    print("ZeroDivisionErrorが発生しました。")
    print(f"Error: {e}")
except TypeError as e:
    print("TypeErrorが発生しました。")
    print(f"Error: {e}")
except ValueError as e:
    print("ValueErrorが発生しました。")
    print(f"Error: {e}")
else:
    print('例外が発生しませんでした。')
finally:
    print('以上')


# 例外が発生しませんでした。
# 以上

上記の処理をフローチャートで表現すると次のようになる。

Notion Image

例外を発生させる

raise文を使うと、自分で例外を発生させることもできる。

raise TypeError('型エラーが発生しました。')


# Traceback (most recent call last):
#   File "C:\Users\yuki\main.py", line 1, in <module>
#     raise TypeError('型エラーが発生しました。')
# TypeError: 型エラーが発生しました。

前述のZeroDivisionError、TypeError、ValueErrorなどはPython標準の組み込み例外だが、標準の例外を継承することでユーザー独自のカスタム例外を作ることもできる。

class CustomExceptinon(Exception):
    pass

raise CustomExceptinon('カスタム例外が発生しました。')


# Traceback (most recent call last):
#   File "C:\Users\yuki\main.py", line 4, in <module>
#     raise CustomExceptinon('カスタム例外が発生しました。')
# CustomExceptinon: カスタム例外が発生しました。

例外のエスカレーション

実行時に例外が発生すると、処理を中断してexcept節を探して呼び出し元にエスカレーションしていく。except節が同メソッド内になければ呼び出し元のメソッドに、そこにもなければさらに上位の呼び出し元のメソッドに…、というように遡って探索する。最上位の呼び出し元まで探してexcept節がなければ、エラーを吐いて処理終了となる。

  • 例外発生箇所とexcept節が同メソッド内にある場合 → 同メソッド内のexcept節から処理を再開
  • def func1():
        func2()
    
    def func2():
        try:
            print(1 / 0)
        except:
            print('func2関数内で例外をキャッチ!')
    
    func1()
    
    
    # func2関数内で例外をキャッチ!
  • 例外発生箇所とexcept節が呼び出し元のメソッドにある場合 → 呼び出し元のメソッド内のexcept節から処理を再開
  • def func1():
        try:
            func2()
        except:
            print('func1関数内で例外をキャッチ!')
    
    def func2():
        print(1 / 0)
        
    func1()
    
    
    # func1関数内で例外をキャッチ!
  • 最上位の呼び出し元まで探してもexcept節がない場合 → exceptがないためエラーを吐いて終了
  • def func1():
        func2()
    
    def func2():
        print(1 / 0)
    
    func1()
    
    
    # Traceback (most recent call last):
    #   File "C:\Users\yuki\main.py", line 7, in <module>
    #     func1()
    #   File "C:\Users\yuki\main.py", line 2, in func1
    #     func2()
    #   File "C:\Users\yuki\main.py", line 5, in func2
    #     print(1 / 0)
    #           ~~^~~
    # ZeroDivisionError: division by zero

    参考資料


    著者画像

    ゆうき

    2018/04からITエンジニアとして活動、2021/11から独立。主な使用言語はPython, TypeScript, SAS, etc.