🫥

Act 08. Pythonでエラーハンドリングを行う

2024/10/27に公開

はじめに

Act 01. AIで外国為替を自動売買するまでの道のりをベースに学習を進めて行く。

コードを書く上でエラーハンドリングはとても重要になってくる。
このシリーズでは外国為替のデータが必要なため、いつかはどこかのAPIを呼び出し為替データを取得することになる。

APIの呼び出しに失敗した場合にエラーハンドリングを行う必要があるため、今回の内容は身につける必要があることは現時点でも明確。とても重要。
何となくは理解しているが、復習もかねてしっかりと記事を書いていこうと思う。

エラーハンドリング

エラーハンドリングはtry-exceptを使うことで実現可能。
例えば以下のコード。

Act08.py
x = 10 / 0

実行結果は以下の通りとなる。
0で割ることは出来ませんよ。と怒られる。

Traceback (most recent call last):
  File "/home/onishi/python/Act08.py", line 1, in <module>
    x = 10 / 0
        ~~~^~~
ZeroDivisionError: division by zero

try-exceptを使うことで以下のようになる。

Act08.py
try:
  x = 10 / 0
  print(x)
except:
  print("0で割れないよ!")

実行結果は以下の通り。
tryの中で何かしらのエラーが発生した場合にexceptに遷移するため、print(x)は実行されない。

0で割れないよ!

実際のエラーメッセージが欲しい場合は以下のように記述することで対応可能。

Act08.py
try:
  x = 10 / 0
  print(x)
except Exception as e:
  print("0で割れないよ!")
  print(e)

出力は以下の通り。

0で割れないよ!
division by zero

x = 10 / "0"に変更した場合はどうなるか。
※数字の0で割るのではなく、文字列の0で割ろうとしている。

Act08.py
try:
  x = 10 / "0"
  print(x)
except Exception as e:
  print("0で割れないよ!")
  print(e)

出力は以下の通り。
タイプがおかしいと怒られる。

0で割れないよ!
unsupported operand type(s) for /: 'int' and 'str'

今回の場合は0で割れないよ!というメッセージではなく、タイプがおかしいよ!にしたい。
どうすればよいか。

それは、エラーをタイプごとに分けることで実現が可能。
exceptの中身をprint(type(e))と変更してみた。

Act08.py
try:
  x = 10 / "0"
  print(x)
except Exception as e:
  print("0で割れないよ!")
  print(type(e))

数値を文字列で割った場合のエラーはTypeErrorであることが判明。
出力は以下の通り。

0で割れないよ!
<class 'TypeError'>

そのため、今回は以下のようにしてみる。
以下のように複数のexceptを定義することで、エラーの型が一致している個所が実行される。

Act08.py
try:
  x = 10 / "0"

except ZeroDivisionError as e:
  print("0で割れないよ!")
  print(e)
  print(type(e))

except TypeError as e:
  print("タイプがおかしいよ!")
  print(e)
  print(type(e))

出力は以下の通り。

タイプがおかしいよ!
unsupported operand type(s) for /: 'int' and 'str'
<class 'TypeError'>

次にfinallyについて。
これは、エラーが発生した後に何かを実行したい場合に使う。
例えば、DBの接続を切る場合や、ファイルを閉じる場合など。

以下のように記述することで使用可能。

try:
  x = 10 / "0"

except ZeroDivisionError as e:
  print("0で割れないよ!")
  print(e)
  print(type(e))

except TypeError as e:
  print("タイプがおかしいよ!")
  print(e)
  print(type(e))

finally:
  print("最後に実行される。")

出力は以下の通り。

タイプがおかしいよ!
unsupported operand type(s) for /: 'int' and 'str'
<class 'TypeError'>
最後に実行される

finallyは正常に処理が終了した場合でも、異常終了した場合でも実行されるため、本当に最後に実行したいことだけを記述するようにする。

補足

一つ気になった。
except内で再度エラーが発生した場合はどうなるのか。

Act08.py
try:
  x = 10 / 0

except ZeroDivisionError as e:
  print("0で割れないよ!")
  print(e)
  print(type(e))

  x = 10 / 0

finally:
  print("最後に実行される。")

出力結果は以下の通り。
finallyの中まで実行されて、その後にさらにエラーメッセージが出力される。

0で割れないよ!
division by zero
<class 'ZeroDivisionError'>
最後に実行される。
Traceback (most recent call last):
  File "/home/onishi/python/Act08.py", line 2, in <module>
    x = 10 / 0
        ~~~^~~
ZeroDivisionError: division by zero

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/home/onishi/python/Act08.py", line 9, in <module>
    x = 10 / 0
        ~~~^~~
ZeroDivisionError: division by zero

During handling of the above exception, another exception occurred:(訳:上記の例外処理中に、別の例外が発生した)が出力された。

finallyも実行されるのは少し以外だったなー。

さいごに

エラーハンドリングは大事だからこそ業務でも使っていたし、特に新鮮味のないものだった。
まあ、新しいことも知れたし良かったとする。

それと、エラーハンドリングといったらログ出力も大事だから、予定とは変わっちゃうけど次回はloggingモジュールについての記事にしようかな。

ではまた

Discussion