はじめに
Java開発者にとって、プログラムの堅牢性を高める上で例外処理は不可欠な要素です。Pythonも同様に、実行時エラーに対処するための強力なメカニズムを提供します。Javaにおけるtry-catchの概念と同様に、Pythonではtry-exceptブロックを用いて予期せぬ問題が発生した際もプログラムの実行を中断させることなく、適切に処理を継続させることが可能です。本稿では、Pythonにおける例外処理の基本的な構文から、一般的なエラーの捕捉、さらには独自の例外クラス定義までを解説します。
基本構文
Pythonの例外処理は、以下の基本的なブロックで構成されます。elseブロックとfinallyブロックはオプションですが、特定のシナリオで非常に役立ちます。
try:
# 例外が発生する可能性のあるコード
except SpecificError1 as e:
# SpecificError1が発生した場合の処理
except SpecificError2 as e:
# SpecificError2が発生した場合の処理
else:
# tryブロックで例外が発生しなかった場合に実行されるコード
finally:
# 例外の有無にかかわらず、常に実行されるコード
tryブロック: 例外が発生する可能性のあるコードを配置します。exceptブロック:tryブロック内で特定の例外が発生した場合に、その例外を捕捉し、対応する処理を実行します。複数のexceptブロックを連ねて、異なる例外タイプに対応できます。elseブロック:tryブロック内で全く例外が発生しなかった場合にのみ実行されます。これは、例外発生時の処理とそうでない時の処理を明確に分離したい場合に便利です。finallyブロック:tryブロックの実行結果(例外の発生有無、exceptブロックやelseブロックの実行有無)にかかわらず、常に実行されます。リソースの解放(ファイルのクローズ、ネットワーク接続の切断など)に利用されます。
一般的な例外の捕捉
Pythonには、さまざまな組み込み例外タイプが用意されています。ここでは、よく遭遇するいくつかの例外とその捕捉方法について例を挙げます。
例えば、数値に変換できない文字列を整数に変換しようとするとValueErrorが発生します。
def safe_int_conversion(input_str: str) -> int | None:
"""
文字列を整数に安全に変換する関数。
変換に失敗した場合はNoneを返す。
"""
try:
converted_value = int(input_str)
print(f"変換に成功しました: {converted_value}")
return converted_value
except ValueError:
print(f"エラー: '{input_str}' は有効な整数ではありません。")
return None
# 使用例
safe_int_conversion("123")
safe_int_conversion("hello")
safe_int_conversion("45.6")
その他の主な組み込み例外:
TypeError: 操作や関数が不適切な型のオブジェクトに適用された場合。IndexError: シーケンスのインデックスが範囲外である場合。KeyError: 辞書に存在しないキーでアクセスしようとした場合。FileNotFoundError: 存在しないファイルを開こうとした場合。ZeroDivisionError: ゼロによる除算が行われた場合。
包括的な例外捕捉
特定の例外タイプを限定せず、あらゆる種類の例外をまとめて捕捉したい場合は、Exceptionクラスを使用します。これはJavaでcatch (Exception e)を使うのと同様の考え方です。これにより、予期しないエラーに対処できますが、エラーの種類を区別しにくくなるため、デバッグが複雑になる可能性があります。可能な限り、具体的な例外タイプを捕捉することが推奨されます。
def perform_risky_operation(data: any):
"""
様々なエラーが発生しうる操作を実行する関数。
"""
try:
# 例外を発生させる可能性のある様々なコード
# 例1: 存在しないキーにアクセス
# dummy_dict = {"a": 1}
# print(dummy_dict["b"])
# 例2: ゼロ除算
# result = 10 / 0
# 例3: 型エラー
# "10" + 5
# 例外が発生しない場合の処理
print(f"操作が正常に完了しました。データ: {data}")
except Exception as general_error:
print(f"予期せぬエラーが発生しました: {general_error}")
# 必要に応じてエラーログを記録するなどの処理
カスタム例外の定義
アプリケーション固有のビジネスロジックに関連するエラーをより明確に表現するために、独自の例外クラスを定義できます。これは、Javaでカスタム例外クラスを作成するプロセスと概念的に非常に似ています。標準のExceptionクラスまたはそのサブクラスを継承することで、簡単にカスタム例外を作成できます。
class InsufficientFundsError(Exception):
"""
残高不足を表すカスタム例外。
"""
def __init__(self, current_balance: float, required_amount: float, message="残高が不足しています。"):
self.current_balance = current_balance
self.required_amount = required_amount
super().__init__(f"{message} (現在残高: {current_balance}, 必要額: {required_amount})")
def __str__(self):
return f"InsufficientFundsError: 現在残高 {self.current_balance} に対して {self.required_amount} が必要です。"
def withdraw(account_balance: float, amount_to_withdraw: float) -> float:
"""
口座から引き出しを行う関数。
残高が不足している場合はInsufficientFundsErrorを発生させる。
"""
if amount_to_withdraw > account_balance:
raise InsufficientFundsError(account_balance, amount_to_withdraw)
return account_balance - amount_to_withdraw
# 使用例
current_account = 100.0
try:
new_balance = withdraw(current_account, 50.0)
print(f"引き出し成功。新しい残高: {new_balance}")
except InsufficientFundsError as e:
print(f"エラーが発生しました: {e}")
try:
new_balance = withdraw(current_account, 150.0)
print(f"引き出し成功。新しい残高: {new_balance}")
except InsufficientFundsError as e:
print(f"エラーが発生しました: {e}")
カスタム例外は、特定のエラー状況に関する詳細情報を含めることができるため、エラー処理のロジックをより洗練させ、コードの可読性と保守性を向上させます。プロジェクトの規模が大きくなるにつれて、適切なカスタム例外の設計は、より堅牢で理解しやすいコードベースを構築するために不可欠となります。