Python例外処理の落とし穴:正しいcatch戦略でバグを防ぐ
最悪のやり方
最もやってはいけないのは、あらゆる例外を捕捉するtry-exceptブロックを書くことです。catch-allとは、例えば以下のようなものです:
try:
...
except:
pass
catch-allブロックが悪い理由:
- 他にどんな例外が発生するか把握できない(詳細は後述)
passで黙って例外を隠蔽しており、エラーがログに残らない
さらに、空のexceptはKeyboardInterrupt(Ctrl+C)、SystemExit、さらにはNameErrorまで捕捉します! つまり以下のコードをクリーンに停止できません:
from time import sleep
while True:
try:
print("Try and stop me")
sleep(1)
except:
print("Don't.. stop.. me now!")
試してみてください。ターミナルウィンドウを閉じるか、Pythonプロセスをkillしないとこのプログラムを止められません。
ややマシなcatch-all
対照的に、except Exceptionを使えば、依然として「手っ取り早く多すぎる例外を捕捉する」方法ですが、少なくとも実行中プロセスを正常に停止できます:
from time import sleep
while True:
try:
print("Try and stop me")
sleep(1)
except Exception:
print("Ok I'll stop!")
Exceptionを捕捉すると、SystemExit、KeyboardInterruptなどは捕捉されません。なぜでしょう?
すべての例外はBaseExceptionクラスを継承しています。公式ドキュメントによれば:「try文で特定のクラスを指定したexcept節がある場合、その節はそのクラスから派生したすべての例外クラスも処理する。」空のexceptはexcept BaseExceptionと等価なので、ありとあらゆる例外を捕捉してしまいます。
一方、Exceptionクラスは:「すべての組み込み非システム終了例外はこのクラスから派生する。ユーザー定義例外もこのクラスから派生すべき。」と定義されています。
さらに悪いケース
以下の例では、osライブラリでカレントワーキングディレクトリを取得しようとしています。しかし、私の太い指がタイポを犯しました:
import os
try:
working_dir = os.getcdw()
print(working_dir)
except:
print('error')
os.getcdwはosモジュールに存在しないため、NameErrorが投げられます。クラッシュする代わりに、except節がエラーを捕捉し、'error'と表示して、プログラムは露骨なタイポにもかかわらず継続してしまいます。残念ながら、これはExceptionを捕捉しても解決しません!
どうやら最初の小技は万能薬ではありません。では、どうすべきか?
扱える例外だけをcatchする
例外についてよく言われるフレーズは:「扱える例外だけをcatchせよ」。多くの開発者は例外をその場で処理したがりますが、実際には自分のプログラムの中で「実際にそれを扱える部分」まで伝播させる方が良いことが多々あります。
例えば、ファイルを開いて読み込むテキストエディタの一部分、OpenFileクラスを考えます。ユーザーが存在しないファイルを開こうとした場合、そのエラーをその場で処理するか、伝播させるかできます。
この場合、呼び出し元に伝播させるのが良いです。なぜならOpenFileには、この例外が呼び出し元にとってどれほど深刻か分からないからです。呼び出し元なら複数の方法で対処できます:
- 同名の新規ファイルを作成して続行
- ファイルが必須なら、ユーザーに「ファイルが存在しない」旨のエラーダイアログを表示
いずれにせよ、FileNotFoundError時にどうするか決めるのはOpenFileクラスの仕事ではありません。
では、例外は常に伝播すべき? いいえ。OpenFileクラスで扱える可能性のある例外としてTimeoutErrorがあります。数回リトライして、呼び出し元をエラーで煩わせずに済ませたい場合があります。これはOpenFileが扱える例外なので、catchしてリトライするのはOKです。
結論
いかなる状況でも、扱える以上の例外をcatchしてはいけません。ブランケットexceptブロックはバグと予測不能なコードの温床です。つまり:扱える例外だけをcatchせよ。
コードを「扱える例外だけをcatchする」マントラで書くなら、catch-allブロックを書くことはすべてのルールを破ることです。だから、お願いです、やめましょう。練習として、既存コードの一部を見直し、この新しい知識で改善できるか試してみてください!
Python
- CMMSトレーニングをマスター:チームを惹きつけ定着させる方法
- 採用担当者必見:ポストパンデミック市場での応募者殺到を制する4つの鍵
- Python 演算子完全マスター: 包括的ガイド
- Pythonエラーと組み込み例外をマスターする
- Pythonカスタム例外マスター:実践ガイド
- Pythonで現在の日付と時刻を取得する実践ガイド
- Python print()関数の実践ガイド:基本から応用まで
- Python辞書:キー/値ペアの追加、更新、管理
- Python print()で改行なし出力:endパラメータとその他のテクニック
- Pythonで平均を計算する:実践ガイド
- Java複数例外処理のマスター:単一catchブロックで複数例外を捕捉