Pythonに慣れたい人向けに、基本のキから解説するシリーズ。
今回の記事では Pythonのコードで
- エラーが発生してもそこでプログラムを終わらずに処理を続ける
- エラーの種類ごとに処理を分ける
などを実現できる「例外処理」について現役のエンジニアが解説しています。
例外処理の基本
エラーの発生をとらえるには if 文ではなく、try-except文を使います。
他言語から来た人は 「try-catch」文などに馴染みのある人は多いでしょう。
使い方
例えば 0(ゼロ)で何かを割ることはできませんから、エラーが発生しますよね。
以下のようなコードではエラーになってプログラムが途中で終了してしまいます。
a = 1 / 0
print(a)
print("処理完了")
# ZeroDivisionError: division by zero
この例では1行目でエラーが発生し、次の行以降は実行されません。
(「処理完了」とは表示されませんね)
1行目で0で割ろうとすると、ZeroDivisionErrorという例外が発生しています。
このエラーを「キャッチ」して、処理を最後まで継続するためには例外処理を実装します。
a = None
try:
a = 1 / 0
except ZeroDivisionError:
print("ゼロ除算エラーが発生")
print("処理が完了")
print(a)
# ゼロ除算エラーが発生
# 処理が完了
# None
このように、処理をtry-except文で囲ってあげます。
except の後には例外名を書きます。
try:
# 通常の処理
except:
# 例外発生時の処理
こうすると、tryブロックの途中でZeroDivisionError例外が発生してもexcept文がキャッチし、
exceptブロックのコードが実行されます。
「処理が完了」の行が実行されてることからも分かるように、たとえ例外が発生してもそのままプログラムが実行され続けています。
関数にすることで、もう少し実用的に書いてみましょう。
def div(a, b):
result = None
try:
result = a / b
except ZeroDivisionError as e:
print("ゼロ除算エラーです")
print(f"エラーメッセージ:{e}")
return result
print(div(10, 5))
# 2
print(div(10, 0))
# ゼロ除算エラーです
# エラーメッセージ:division by zero
# None
except文はZeroDivisionError例外が発生したときにだけ実行され、例外が発生しても処理が続いている(return文でNoneが返ってきている)のが分かると思います。
すべての例外をキャッチする
上の例では0除算のエラー(ZeroDivisionError)の例外が発生したときはうまくキャッチできますが、他の例外には対応できません。
以下の例では、リスト(list)型の変数の範囲外にアクセスしようとして、IndexError例外を発生させます。
a = ['alpha', 'bravo', 'charlie']
target = None
try:
target = a[10]
except IndexError as e:
print("指定されたキーは範囲外です")
print(f"エラーメッセージ:{e}")
print(target)
# 指定されたキーは範囲外です
# エラーメッセージ:list index out of range
# None
この場合、IndexError例外が発生するのが分かっていれば上のようなコードで大丈夫ですが、もし他の例外が起こった場合はプログラムが停止します。
すべての例外をキャッチしたいときは、以下のようにExceptionクラスを指定します。
try:
target = a[10]
except Exception as e:
print(f"エラーメッセージ:{e}")
# エラーメッセージ:list index out of range
Exceptionクラスは(ほとんど)すべての例外の既定クラスです。
そのため、どのような例外でもキャッチすることができます。
どんな例外が発生しても必ず exceptブロックを実行させたいときや、想定できない例外が発生する可能性が高いときに使います。
ただしExceptionクラスのように、どのような例外もキャッチしてしまうと、プログラムの隠れたバグの原因になることも。後々にデバッグを難しくする原因にもなります。できるだけ具体的な例外クラスを指定するようにし、Exceptionを乱用するのは控えましょう。
例外を表すクラスはさまざまですが、Pythonの組み込みで指定されている例外は公式のドキュメントで確認できます。
複数の例外をキャッチする
では、具体的にいろいろな例外をキャッチしたいときは、どのようにコードを書けばいいのでしょう?
答えは単純で、以下のようにexcept文をいくつも指定すればOKです。
def get_item(l, d):
target = None
try:
target = l[d]
except IndexError as e:
print("指定されたキーは範囲外です")
print(f"エラーメッセージ:{e}")
except TypeError as e:
print("指定されたキーの形式は正しくありません")
print(f"エラーメッセージ:{e}")
return target
item = get_item([1, 2, 3], 99)
print(item)
# 指定されたキーは範囲外です
# エラーメッセージ:list index out of range
# None
item = get_item([1, 2, 3], 'a')
print(item)
# 指定されたキーの形式は正しくありません
# エラーメッセージ:list indices must be integers or slices, not str
# None
上のサンプルではIndexErrorとTypeErrorという2つの例外をそれぞれキャッチしているのが分かります。
もし、この2つのどちらでもない例外(想定できない)を捕捉したいときは、except Exceptionを追加してあげましょう。
例外がなかったときだけ実行する
「例外が発生したときの例外処理は分かったけど、例外が発生しなかったときだけ実行するコードはどう書けばいいの?」と思うかもしれません。
そんなときはelse文を使います。
elseはif文でも使いましたね。
def div_else(a, b):
result = None
try:
result = a / b
except ZeroDivisionError as e:
print("ゼロ除算エラーです")
print(f"エラーメッセージ:{e}")
else:
print("正常に完了")
return result
print(div_else(1, 2))
# 正常に完了
# 0.5
print(div_else(1, 0))
# ゼロ除算エラーです
# エラーメッセージ:division by zero
# None
このサンプルでは、print(“正常に完了”) の文は例外が発生しなかったときにだけ実行されています。
try文の中で例外が起きなかったとき(つまりexcept文にキャッチされなかったとき)にだけelseブロックの中が実行されます。
例外に関わらず必ず実行する
また、例外が発生したかどうかに関わらず、絶対に実行したい処理があるとしましょう。
そんなときは finally 文を使います。
def div_fin(a, b):
result = None
try:
result = a / b
except ZeroDivisionError as e:
print("ゼロ除算エラーです")
print(f"エラーメッセージ:{e}")
finally:
print("最後に必ずする処理")
return result
print(div_fin(1, 2))
# 最後に必ずする処理
# 0.5
print(div_fin(1, 0))
# ゼロ除算エラーです
# エラーメッセージ:division by zero
# 最後に必ずする処理
# None
上のサンプルでは、例外に関わらず print(“最後に必ずする処理”) の文が実行されています。
finally文は、メインとなる処理が終わったあとに、後処理をするときによく使われます。例えば、データベースとの接続を終了したり、ファイルに書き込みを行ったりなどです。
どんなときに例外処理を書くの?
Pythonに限りませんが、プログラミングをしていく上で例外処理は避けては通れないくらい重要です。
上で見たように、0で割り算をしたり範囲外の値を取得しようとしたりする(重大なバグのある)コード以外でも、頻繁に例外と向き合うはずです。
例えば、以下のようなエラーはとても一般的です
- 整数を入力してほしいフォームに、ユーザーが文字列を入力した
- インターネットからファイルをダウンロードする処理で、ファイルがすでに削除されていた
- 処理途中のファイルが消されてしまった
このような例外が発生するたびにプログラムが強制終了していては、とんでもなく不便なプログラムになってしまいます。
そのため、例外が起きてもプログラムを途中で止めたくないときは、例外処理をきちんと実装する必要があるのです。
Pythonの学習法について
Python の勉強が辛くなっていませんか?
Pythonは比較的取り組みやすい言語と言われていますが、プログラミング初心者にとっては分からないことだらけ。
ゼロから独学で勉強するのは厳しい道のりです。
今回、様々な現場、システム、言語を経験してきた現役エンジニアの立場から、初心者でも挫折しない学習方法を解説する記事を書きました。もちろん、お金をかけずに習得できる方法も解説しています。
できるだけストレスがかからない勉強法を解説しているので、ぜひ参考にしてみてくださいね。
まとめ
Pythonでエラーが発生したときに処理を止めないためには、if文ではなく、try-except文の例外処理を実装することを確認しました。