Q1次のコードの出力として正しいものはどれですか?try:
raise ValueError("NG")
print("A")
except ValueError as e:
print(e)
print("B")
raise で例外を発生させる / 自作例外クラス
Python の raise で例外を発生させる方法と、自作(カスタム)例外クラスの作り方を図解で解説します。実装パターンまで一通り押さえます。
前回の try / except では、発生した例外を受け止める側を学びました。
この記事で扱う raise は、自分から例外を発生させる側の書き方です。不正な入力や想定外の状態を検出したときに raise で例外を投げると、呼び出し側が try / except で確実に気づけるようになります。
raise の基本
raise 例外クラス("メッセージ") と書くと、その場で例外を発生させられます。raise が実行された瞬間、以降のコードはスキップされ、対応する except ブロックに処理が飛びます。
メッセージ部分は省略できますが、どんな理由で失敗したかを付けておくと、受け取った側が except ... as e から内容を読み取れます。
# raise で ValueError を発生させ、呼び出し側で受け止める
try:
raise ValueError("年齢が不正です")
print("ここには到達しない")
except ValueError as e:
print(f"キャッチ: {e}")
# 出力: キャッチ: 年齢が不正です
# 例外クラスは Python 組み込みのものがそのまま使える
# よく使うもの:ValueError / TypeError / KeyError / IndexError など
if でバリデーションして raise する
raise が最もよく使われるのは、入力値の検査です。if で不正な条件を見つけたら、その場で raise を投げて、呼び出し側に判断を返します。これは後述する関数でよく用いられる手法で、関数の中で引数の不正を検出したときに呼び出し側へ通知する定番のやり方になります。
正常なら普通に処理が流れる、異常なら即 raise で中断するという流れは、実務のバリデーション実装の基本パターンです。
# 商品価格のバリデーション:0 以下はエラー扱い
price = -500
try:
if price < 0:
raise ValueError(f"価格は 0 以上にしてください(受け取った値: {price})")
if not isinstance(price, int):
raise TypeError(f"価格は整数にしてください(受け取った型: {type(price).__name__})")
print(f"登録完了: {price} 円")
except ValueError as e:
print(f"値エラー: {e}")
except TypeError as e:
print(f"型エラー: {e}")
# 出力: 値エラー: 価格は 0 以上にしてください(受け取った値: -500)
組み込み例外の使い分けの目安は次の通りです。値の中身がおかしいなら ValueError、値の種類(型)が想定と違うなら TypeError、辞書やリストに目的のデータが無いなら KeyError / IndexError が自然です。
自作例外クラスを定義する
組み込みの例外だけでは「どのバリデーションで落ちたか」が区別しづらいことがあります。そんなときは、自分専用の例外クラスを用意すると、except 側でピンポイントに受け止められるようになります。
作り方は class 例外名(Exception): と書いて、中身は pass(空)にするだけです。class の詳しい話は後のオブジェクト指向の章で扱いますが、例外クラスを作るだけならこの 1 行で十分です。
# 独自の例外クラスを定義(1 行で OK)
class InvalidAgeError(Exception):
pass
class DuplicateUserError(Exception):
pass
registered_users = ["tanaka", "suzuki"]
new_user = "tanaka"
new_age = -1
try:
if new_age < 0:
raise InvalidAgeError(f"年齢は 0 以上: {new_age}")
if new_user in registered_users:
raise DuplicateUserError(f"登録済み: {new_user}")
print(f"登録完了: {new_user}")
except InvalidAgeError as e:
print(f"年齢エラー: {e}")
except DuplicateUserError as e:
print(f"重複エラー: {e}")
# 出力: 年齢エラー: 年齢は 0 以上: -1
独自例外は Exception を親クラスに指定する
括弧の中の Exception は親となるクラスを指定しています。Exception を親に置くことで、メッセージを保持できる、except Exception でまとめて受け止められる、といった例外の標準機能が自作クラスにも自動で備わります。親子関係(継承)の仕組みそのものはオブジェクト指向の章で詳しく扱いますが、例外を作るだけならこの 1 行で十分です。
この記事では、raise で自分から例外を発生させる方法、if + raise を組み合わせたバリデーションパターン、そして Exception を親に指定した自作例外クラスの作り方を学びました。
次の記事では、ここまで何度か顔を出した def を正面から扱い、処理をまとめて再利用する関数を学びます。
理解度チェック
まずは1問ずつ答えてみましょう。
Q2値の中身が不正なときに投げる組み込み例外として最も適切なものはどれですか?
Q3自作例外 class MyError(Exception): pass を使う最大のメリットはどれですか?