【初心者向け】Pythonで独自例外を作成する方法

Pythonでは、プログラム実行中にエラーが発生すると例外(Exception)が発生し、処理が中断されます。

標準で用意された例外だけでなく、アプリケーションの要件に合わせて「独自例外」を作成することで、エラー原因をより明確にし、保守性や可読性を向上させることができます。

本記事では、初級者向けに例外処理の基本から独自例外の作成方法、使い方、演習問題と解答例までを丁寧に解説します。


例外処理の基本

try–except–finally 構文

try:
    # エラーが発生するかもしれない処理
    result = 10 / 0
except ZeroDivisionError as e:
    # ZeroDivisionError が発生したときの処理
    print(f"エラーが発生しました: {e}")
finally:
    # 例外の有無にかかわらず必ず実行される処理
    print("処理を終了します")
  • try ブロック:通常処理を記述
  • except ブロック:特定の例外発生時の処理
  • finally ブロック:後片付け等、必ず実行したい処理

except の応用

複数の例外をまとめてキャッチしたり、例外オブジェクトに名前を付けて詳しい情報を取得できます。

try:
    x = int(input("整数を入力してください: "))
    print(10 / x)
except (ValueError, ZeroDivisionError) as err:
    print(f"入力または計算エラー: {err}")

独自例外を作成するメリット

  • 可読性の向上
    意図にあった例外クラス名にすることで、何のエラーか一目瞭然になる。
  • 細かいエラー管理
    既存の例外と区別してcatchできる。
  • 再利用性
    プロジェクト内で共通的に使える例外クラスとしてまとめられる。

独自例外の作り方

  1. Exceptionクラスを継承
  2. 必要に応じて __init__ や属性を定義
  3. raise文で発生させる

シンプルな独自例外

class MyError(Exception):
    """汎用的な独自例外クラス"""
    pass

def func(x):
    if x < 0:
        # 条件を満たしたら MyError を発生
        raise MyError("x は 0 以上でなければなりません")
    return x

try:
    func(-5)
except MyError as e:
    print(f"独自例外キャッチ: {e}")

詳細情報を持たせる例外

class ValidationError(Exception):
    def __init__(self, message, value):
        super().__init__(message)
        self.value = value  # 問題となった値を保持

def validate_age(age):
    if age < 0 or age > 120:
        raise ValidationError("年齢の範囲が不正です", age)

try:
    validate_age(150)
except ValidationError as e:
    print(f"{e}: 問題の値 = {e.value}")

実践例:ユーザー入力のバリデーション

以下は、ユーザーIDとして正の整数のみを許可する例です。

class InvalidUserIDError(Exception):
    """ユーザーIDが不正な場合に発生する例外"""
    def __init__(self, user_id):
        super().__init__(f"ユーザーID {user_id} は正の整数でなければなりません")
        self.user_id = user_id

def register_user(user_id):
    if not isinstance(user_id, int) or user_id <= 0:
        raise InvalidUserIDError(user_id)
    print(f"ユーザー {user_id} を登録しました")

# 使用例
try:
    register_user(-1)
except InvalidUserIDError as e:
    print(f"登録失敗: {e}")

演習問題

問題1

ユーザー名として、3文字以上20文字以下かつ英数字のみを許可する InvalidUsernameError クラスを作成し、バリデーション関数 validate_username(username) を実装してください。

問題2

ポート番号として 1~65535 の整数のみを許可する InvalidPortError クラスを作成し、関数 check_port(port) を実装してください。

無効な場合は例外を発生させ、呼び出し側でキャッチしてエラーメッセージを表示してください。


解答例

解答1

class InvalidUsernameError(Exception):
    def __init__(self, username):
        super().__init__(f"ユーザー名『{username}』は3文字以上20文字以下の英数字で入力してください")
        self.username = username

import re

def validate_username(username):
    if not (3 <= len(username) <= 20):
        raise InvalidUsernameError(username)
    if not re.match(r'^[A-Za-z0-9]+$', username):
        raise InvalidUsernameError(username)
    return True

# テスト
for name in ["ab", "validUser123", "invalid_user!", "thisnameiswaytoolong123"]:
    try:
        validate_username(name)
        print(f"{name}: OK")
    except InvalidUsernameError as e:
        print(f"{name}: {e}")

解答2

class InvalidPortError(Exception):
    def __init__(self, port):
        super().__init__(f"ポート番号 {port} は 1~65535 の範囲で指定してください")
        self.port = port

def check_port(port):
    if not isinstance(port, int) or not (1 <= port <= 65535):
        raise InvalidPortError(port)
    print(f"ポート {port} を使用します")

# テスト
for p in [0, 22, 70000, "80"]:
    try:
        check_port(p)
    except InvalidPortError as e:
        print(f"エラー: {e}")

まとめ

  • 例外処理 でプログラムの安定性を確保
  • 独自例外 を作ることで、エラー原因が明確に
  • 演習問題 で理解を深める

今回学んだ独自例外の作成方法を活用し、より堅牢で読みやすいコードを目指しましょう!