sec03 - Pythonで学ぶカスタム例外:基本から実践まで
スポンサーリンク

カスタム例外とは何か

Pythonでは、プログラムの途中で何らかの異常が発生したときに「例外(Exception)」という仕組みが使われます。たとえば、ゼロで割ろうとしたり、存在しないファイルを開こうとしたりすると、Pythonは自動的に例外を発生させます。

しかし、現実のプログラム開発では、標準で用意された例外だけでは足りないことがあります。たとえば、「入力データが不正」や「APIの応答が想定外」など、アプリケーション独自のエラーを表現したい場面があるのです。

そのようなときに役立つのがカスタム例外(ユーザー定義例外)です。これは自分で新しい例外クラスを定義して、特定の状況で使えるようにする仕組みです。

クラス

クラスについては専用のレクチャーで学習しますが、カスタム例外は基本の形さえ覚えてしまえば使うことができます。まずは、このレクチャーで紹介する最小限のカスタム例外を使用してみてください。

そのうえで、更に詳細なカスタム例外を作りたくなった場合は、クラスについて学び、その後でこのレクチャーに戻ってきていただければと思います。

基本形:最もシンプルなカスタム例外

Pythonのすべての例外は、Exceptionクラスを基礎にしています。したがって、新しい例外を作りたいときは、Exceptionを継承したクラスを定義するだけです。

# カスタム例外の基本形
class MyError(Exception):
    pass

# カスタム例外を発生させる
raise MyError('これはカスタム例外の例です。')

実行結果:

Exception has occurred: MyError
これはカスタム例外の例です。
  File "C:\xxxxx\sec03_ctrl_flow_exception.py", line 6, in <module>
    raise MyError('これはカスタム例外の例です。')
MyError: これはカスタム例外の例です。

このように、MyErrorという独自の例外を作り、raise文で発生させることができます。Pythonの標準例外と同じように、エラーメッセージとともに表示されます。

スポンサーリンク

カスタム例外を捕まえる

カスタム例外も、通常の例外と同じように try / except 構文で捕まえることができます。

class MyError(Exception):
    pass

try:
    raise MyError('何か問題が発生しました。')
except MyError as e:
    print('カスタム例外を捕まえました:', e)

実行結果:

カスタム例外を捕まえました: 何か問題が発生しました。

このように、通常の例外と同じ感覚で自作の例外を処理することができます。

属性を持つカスタム例外

単にメッセージを持たせるだけでなく、エラーに関連する情報(エラーコードや入力値など)を属性として保存しておくこともできます。

class ValidationError(Exception):
    def __init__(self, field, message):
        self.field = field
        self.message = message
        super().__init__(f'{field} にエラーがあります: {message}')

# 使用例
try:
    raise ValidationError('ユーザー名', '3文字以上で入力してください。')
except ValidationError as e:
    print('エラーの発生箇所:', e.field)
    print('エラーメッセージ:', e.message)

実行結果:

エラーの発生箇所: ユーザー名
エラーメッセージ: 3文字以上で入力してください。

このようにして、例外に「どの項目でエラーが起きたのか」などの情報を含めることができます。現実のアプリケーション開発ではとてもよく使われるテクニックです。

共通の親クラスを作る

アプリケーションが大きくなってくると、複数の種類のエラーを定義したくなります。たとえば「入力エラー」「データベースエラー」「通信エラー」などです。

そのときに、すべてのカスタム例外の親クラスを1つ作っておくと便利です。これにより、すべてのアプリケーション固有のエラーをまとめて扱うことができます。

# 共通の親クラス
class ProjectError(Exception):
    '''アプリケーション固有の例外の基底クラス'''
    pass

# 具体的なエラー
class InputError(ProjectError):
    pass

class DatabaseError(ProjectError):
    pass

try:
    raise InputError('入力が不正です。')
except ProjectError as e:
    print('共通の親クラスで捕まえました:', e)

実行結果:

共通の親クラスで捕まえました: 入力が不正です。

このように、ProjectErrorを基底クラスにしておくと、後からどんな種類のエラーが増えてもまとめて処理できるようになります。

例外を再スローする(raise文の再利用)

例外を捕まえたあとで、もう一度外側に伝えたいことがあります。これを「再スロー」と呼びます。

class MyError(Exception):
    pass

def process():
    try:
        raise MyError('内部処理で問題が発生しました。')
    except MyError as e:
        print('内部で例外を捕まえましたが、外に再スローします。')
        raise  # 例外を再スロー

try:
    process()
except MyError as e:
    print('外側で再スローされた例外を捕まえました:', e)

実行結果:

内部で例外を捕まえましたが、外に再スローします。
外側で再スローされた例外を捕まえました: 内部処理で問題が発生しました。

このように、内部関数で一度例外を処理しつつ、外に情報を伝えることができます。

カスタム例外を使うメリット

  • エラーの意味が明確になる(例:「入力値エラー」と「通信エラー」を区別できる)
  • 例外ごとに異なる処理ができる(例:ログを出すか、ユーザーに通知するか)
  • コードの可読性と保守性が向上する
  • 共通の親クラスで一括処理ができる

これらの利点により、カスタム例外は中規模以上のアプリケーションではほぼ必須といっても過言ではありません。

スポンサーリンク