Pythonの例外処理ベストプラクティス15選|try exceptの書き方と「try catch」との違い

Pythonでプログラムを開発していると、「ファイルが存在しない」「数値に変換できない」「ネットワーク接続に失敗した」といった予期しないエラーが発生することがあります。
こうした問題に適切に対応するために重要なのが例外処理です。
Pythonではtry、except、else、finallyなどを使って例外を処理できます。しかし、例外処理は「とりあえずexceptを書けばよい」というものではありません。
この記事では、Pythonの例外処理におけるベストプラクティスについて、基本的な書き方から実践的な設計方法、やってはいけない例まで詳しく解説します。
Pythonの例外処理とは?
例外処理とは、プログラムの実行中に発生した異常を適切に処理する仕組みです。
例えば、次のコードを実行するとします。
number = int(input("数字を入力してください:"))
print(number)
ここでユーザーがabcと入力すると、int()で文字列を整数に変換できないため、ValueErrorが発生します。
ValueError: invalid literal for int() with base 10: 'abc'
このような予期しない処理の中断を防ぐために、例外処理を利用します。
try:
number = int(input("数字を入力してください:"))
except ValueError:
print("数字を入力してください。")
これにより、ValueErrorが発生した場合でもプログラムを適切に処理できます。
Pythonの例外処理の基本構文
Pythonの例外処理では、主に次の4つを使用します。
-
try -
except -
else -
finally
基本的な構文は次のとおりです。
try:
# 例外が発生する可能性のある処理
処理
except 例外の種類:
# 例外が発生した場合の処理
処理
else:
# 例外が発生しなかった場合の処理
処理
finally:
# 例外の有無に関係なく実行する処理
処理
ベストプラクティス1:具体的な例外を指定する
Pythonの例外処理では、できるだけ具体的な例外クラスを指定することが重要です。
例えば、次のようなコードがあります。
try:
number = int(input())
except:
print("エラーが発生しました")
これは一見便利ですが、あまりおすすめできません。
なぜなら、どのような例外が発生しても捕捉してしまうからです。
例えば、プログラムのバグによって発生した例外まで隠してしまう可能性があります。
そこで、具体的な例外を指定します。
try:
number = int(input())
except ValueError:
print("整数を入力してください")
これなら、ValueErrorだけを処理できます。
よく使われる例外
| 例外 | 主な発生原因 |
|---|---|
ValueError |
値が不正 |
TypeError |
型が不適切 |
KeyError |
辞書に存在しないキー |
IndexError |
リストなどの範囲外アクセス |
FileNotFoundError |
ファイルが存在しない |
ZeroDivisionError |
0による除算 |
AttributeError |
存在しない属性へのアクセス |
ImportError |
モジュールのインポートに失敗 |
ベストプラクティス2:except Exceptionを安易に使わない
次のようなコードもよく見かけます。
try:
process()
except Exception:
print("エラーが発生しました")
Exceptionは多くの通常の例外をまとめて捕捉できます。
ただし、これを多用するとどんなエラーが発生したのか分からなくなるという問題があります。
例えば、
try:
data = int(value)
except Exception:
print("エラー")
よりも、
try:
data = int(value)
except ValueError:
print("数値に変換できません")
のように、処理する例外を明確にしたほうが保守性が高くなります。
ベストプラクティス3:tryの範囲を小さくする
例外処理では、tryブロックに大量の処理を入れないことも重要です。
例えば、次のコードです。
try:
data = load_data()
data = process_data(data)
save_data(data)
send_notification()
except Exception:
print("エラーが発生しました")
これでは、どの処理で例外が発生したのか分かりにくくなります。
必要な処理だけをtryに入れるほうが分かりやすくなります。
try:
data = load_data()
except FileNotFoundError:
print("ファイルがありません")
return
data = process_data(data)
try:
save_data(data)
except PermissionError:
print("ファイルを書き込めません")
このようにすると、どの処理で、どの例外を処理しているのかが明確になります。
ベストプラクティス4:例外を握りつぶさない
次のようなコードは避けましょう。
try:
process()
except Exception:
pass
これは例外を無視してしまいます。
問題が発生しているにもかかわらず、何も表示されないため、障害の原因を調査することが難しくなります。
どうしても例外を捕捉する必要がある場合でも、ログを残すなどの対応を検討しましょう。
import logging
try:
process()
except Exception:
logging.exception("処理中にエラーが発生しました")
logging.exception()を使うと、例外情報を含めたログを記録できます。
詳しくは「Python traceback.print_exc()とは?エラーの発生場所を表示する方法」で解説しています。
ベストプラクティス5:エラーメッセージを分かりやすくする
例外が発生した際には、単に「エラー」と表示するだけではなく、何が問題だったのかを伝えることが大切です。
悪い例:
except ValueError:
print("エラー")
改善例:
except ValueError:
print("年齢には整数を入力してください。")
ユーザー向けのメッセージと、開発者向けのログを分けるのも有効です。
import logging
try:
age = int(value)
except ValueError:
logging.exception("年齢の変換に失敗しました")
print("年齢には整数を入力してください。")
ベストプラクティス6:elseを適切に使う
elseは、tryの処理が正常に終了した場合に実行されます。
例えば、ファイルを読み込む処理では次のように書けます。
try:
with open("data.txt", encoding="utf-8") as file:
data = file.read()
except FileNotFoundError:
print("ファイルがありません")
else:
print("ファイルの読み込みに成功しました")
print(data)
elseを使うことで、
-
例外が発生する可能性がある処理
-
正常終了した後に行う処理
を分けられます。
ベストプラクティス7:後片付けにはfinallyを使う
finallyは、例外が発生したかどうかに関係なく実行されます。
try:
process()
except ValueError:
print("値が不正です")
finally:
print("処理を終了します")
finallyは、ファイルやデータベース接続などのリソースを確実に解放したい場合などに利用されます。
ただし、ファイル操作ではwith構文を利用できる場合が多いため、積極的に活用しましょう。
with open("data.txt", encoding="utf-8") as file:
data = file.read()
withを使うことで、処理終了時のリソース管理を安全に行えます。
ベストプラクティス8:複数の例外を適切に処理する
異なる例外に対して処理を変えたい場合は、exceptを複数記述できます。
try:
number = int(value)
result = 100 / number
except ValueError:
print("整数を入力してください")
except ZeroDivisionError:
print("0で割ることはできません")
これにより、エラーの種類に応じて適切なメッセージを表示できます。
ベストプラクティス9:複数の例外をまとめて処理する
複数の例外に対して同じ処理を行う場合は、タプルで指定できます。
try:
process()
except (ValueError, TypeError):
print("入力値が不正です")
この方法なら、同じ処理を何度も書く必要がありません。
ベストプラクティス10:as eで例外情報を取得する
例外オブジェクトを取得したい場合は、asを使います。
try:
number = int("abc")
except ValueError as e:
print(e)
実行すると、例外の具体的な内容を取得できます。
invalid literal for int() with base 10: 'abc'
ただし、ユーザーにそのまま例外メッセージを表示するのではなく、ユーザー向けのメッセージと開発者向けのログを分けることも重要です。
ベストプラクティス11:例外を再発生させる
例外を捕捉した後、上位の処理に例外を伝えたい場合はraiseを使用します。
try:
process()
except ValueError:
print("処理に失敗しました")
raise
raiseだけを書くと、現在処理している例外をそのまま再発生させられます。
これは、下位レイヤーでログを記録しつつ、上位レイヤーでもエラー処理を行いたい場合などに利用できます。
ベストプラクティス12:独自例外を作る
アプリケーション固有のエラーを扱う場合は、独自の例外クラスを定義できます。
class InsufficientBalanceError(Exception):
pass
そして、
def withdraw(balance, amount):
if amount > balance:
raise InsufficientBalanceError("残高が不足しています")
return balance - amount
呼び出し側では、
try:
balance = withdraw(1000, 2000)
except InsufficientBalanceError:
print("残高が不足しています")
のように処理できます。
大規模なアプリケーションでは、独自例外を適切に設計することで、エラー処理の意図が明確になります。
ベストプラクティス13:raise ... fromで原因を明確にする
別の例外に変換したい場合は、raise ... fromが便利です。
try:
data = int(value)
except ValueError as e:
raise RuntimeError("データ処理に失敗しました") from e
これにより、元の例外を原因として保持したまま、アプリケーション側に分かりやすい例外を渡せます。
ベストプラクティス14:EAFPの考え方を理解する
Pythonでは、EAFP(Easier to Ask Forgiveness than Permission)という考え方がよく使われます。
直訳すると、
許可を求めるより、許しを求めるほうが簡単
という意味です。
例えば、辞書にキーが存在するかを事前に確認する方法があります。
if "name" in data:
print(data["name"])
一方、Pythonでは次のように書くこともできます。
try:
print(data["name"])
except KeyError:
print("nameがありません")
このように、まず処理を実行し、問題が発生したら例外として処理するという考え方がEAFPです。
Pythonではよく使われる設計思想ですが、すべての場合に例外処理を使うべきという意味ではありません。
ベストプラクティス15:LBYLとの違いを理解する
EAFPと対比される考え方にLBYL(Look Before You Leap)があります。
これは「飛ぶ前に確認する」という意味です。
例えば、
if key in data:
value = data[key]
else:
value = None
のように、処理を実行する前に条件を確認します。
一方、EAFPでは、
try:
value = data[key]
except KeyError:
value = None
とします。
どちらが正しいというものではなく、状況に応じて使い分けることが重要です。
Pythonの例外処理でやってはいけないこと
ここまでの内容を踏まえると、次のようなコードはできるだけ避けるべきです。
何でも捕捉する
try:
process()
except:
pass
詳しくは「Python SystemExitとは?sys.exit()で起きる例外を解説」で解説しています。
例外を無視する
try:
process()
except Exception:
pass
tryの範囲を広げすぎる
try:
process_a()
process_b()
process_c()
process_d()
process_e()
except Exception:
print("エラー")
エラー内容を記録しない
except Exception:
print("エラー")
特に本番環境では、ログを残さないと障害発生時の原因調査が困難になります。
Pythonの例外処理でおすすめの書き方
実務では、例えば次のような形が使いやすいでしょう。
import logging
def load_number(value):
try:
return int(value)
except ValueError:
logging.exception("数値への変換に失敗しました")
raise ValueError("正しい整数を指定してください")
ポイントは、
-
例外を具体的に指定する
-
必要な範囲だけ
tryに入れる -
ログを残す
-
必要に応じて例外を再発生させる
-
ユーザー向けメッセージを分かりやすくする
という点です。
Pythonの例外処理は「エラーを隠す」ためではない
例外処理を学ぶ際に特に重要なのが、例外処理はエラーを隠すための機能ではないということです。
例えば、
try:
process()
except Exception:
print("処理に失敗しました")
だけでは、問題の根本的な原因が分からなくなる可能性があります。
例外処理の目的は、
「予期される異常を適切に処理し、予期しないバグは発見できる状態にする」
ことです。
そのため、何でもexceptで捕まえるのではなく、どの例外をどこで処理するのかを設計することが重要です。
Python例外処理のベストプラクティスまとめ
Pythonの例外処理で特に意識したいポイントをまとめます。
| ポイント | ベストプラクティス |
|---|---|
| 例外指定 | 具体的な例外クラスを指定する |
tryの範囲 |
必要最小限にする |
except |
安易にExceptionを使わない |
| エラー無視 | passで握りつぶさない |
| ログ | 必要に応じて例外情報を記録する |
else |
正常終了時の処理に利用する |
finally |
必ず実行したい後処理に利用する |
with |
リソース管理に積極的に利用する |
| 独自例外 | アプリケーション固有のエラーに利用する |
raise |
必要に応じて上位へ例外を伝える |
| EAFP | Pythonらしい例外処理の考え方として理解する |
まとめ:Pythonの例外処理は「適切に捕捉する」ことが重要
Pythonの例外処理では、単純にtryとexceptを書くだけではなく、どの例外を、どの範囲で、どのレイヤーで処理するかを考えることが重要です。
特に覚えておきたいのは次の5点です。
-
具体的な例外を指定する
-
tryの範囲を小さくする -
例外を握りつぶさない
-
必要に応じてログを残す
-
raiseや独自例外を適切に活用する
初心者のうちは「エラーが出たからexcept Exceptionで処理する」と考えがちですが、例外処理を正しく設計できるようになると、保守性が高く、障害にも強いPythonプログラムを書けるようになります。
特に実務では、例外処理を「エラーを消す技術」ではなく、プログラムの異常を安全に扱うための設計技術として捉えることが大切です。
Pythonに「try catch」はありません
JavaやC#、JavaScriptから来た方が最初につまずく点です。
Pythonでは catch というキーワードを使いません。同じ役割を果たすのは except です。
# Java / C# / JavaScript の書き方(Pythonでは動きません)
try {
// 処理
} catch (Exception e) {
// エラー時の処理
}
# Python の書き方
try:
# 処理
except Exception as e:
# エラー時の処理
catch と書くと SyntaxError になります。「try catch Python」で検索してたどり着いた方は、except に読み替えてください。
他の言語との対応表
| やりたいこと | Java / C# / JS | Python |
|---|---|---|
| 例外を捕まえる | catch | except |
| 必ず実行する | finally | finally(同じ) |
| 例外を投げる | throw | raise |
| 例外が出なかったとき | (無い) | else(Python独自) |
throw も使えません。Pythonでは raise です。逆に else は他の言語にないPython独自の構文で、「例外が起きなかった場合だけ実行する処理」を書けます。
言語ごとの書き方の違いは似たプログラミング言語の違い、実際に出るエラーの読み方はPythonエラー一覧と対処法にまとめています。
例外クラスの個別解説
- PythonのExceptionとBaseExceptionの違い|自作例外とraise from
- Python ConnectionRefusedError(Errno 111・61・10061)
- Python ConnectionResetError(Errno 104・54・10054)
- Python IsADirectoryError(Errno 21)
- Python NotADirectoryError(Errno 20)
- Python UnicodeEncodeErrorとは?原因と対処法を初心者向けに解説
- Python NotImplementedErrorとは?未実装を示す例外の使い方
- Python TimeoutErrorとは?タイムアウト例外の種類と対処法
- Python ConnectionErrorとは?接続失敗の原因と対処法を解説
- Python EOFErrorとは?入力が尽きたときの原因と対処法
- PythonのIOErrorとは?OSErrorとの関係と対処法を解説
- Python SystemExitとは?sys.exit()で起きる例外を解説






