はじめに
プログラムは予期しないエラーに遭遇することがあります。例外処理を適切に行うことで、エラー発生時も graceful に対応できます。
基本的な例外処理
try-except文
try:
result = 10 / 0
except ZeroDivisionError:
print("0で割ることはできません")
# プログラムは続行される
print("処理を続けます")
複数の例外をキャッチ
try:
value = int(input("数字を入力: "))
result = 10 / value
except ValueError:
print("数字を入力してください")
except ZeroDivisionError:
print("0以外を入力してください")
# まとめてキャッチ
try:
value = int(input("数字を入力: "))
result = 10 / value
except (ValueError, ZeroDivisionError) as e:
print(f"エラー: {e}")
例外オブジェクトの取得
try:
with open("nonexistent.txt") as f:
content = f.read()
except FileNotFoundError as e:
print(f"ファイルが見つかりません: {e.filename}")
print(f"エラーメッセージ: {e}")
else と finally
try:
result = 10 / 2
except ZeroDivisionError:
print("エラー発生")
else:
# 例外が発生しなかった場合のみ実行
print(f"結果: {result}")
finally:
# 例外の有無に関わらず必ず実行
print("処理完了")
finallyの活用例
def read_file(path):
f = None
try:
f = open(path, "r")
return f.read()
except FileNotFoundError:
return None
finally:
if f:
f.close() # 必ずファイルを閉じる
主要な組み込み例外
| 例外 | 説明 |
|---|---|
Exception | ほぼ全ての例外の基底クラス |
ValueError | 値が不正 |
TypeError | 型が不正 |
KeyError | 辞書にキーが存在しない |
IndexError | インデックスが範囲外 |
FileNotFoundError | ファイルが見つからない |
AttributeError | 属性が存在しない |
ImportError | インポートに失敗 |
RuntimeError | 実行時エラー |
# よくある例外の例
try:
d = {"a": 1}
print(d["b"]) # KeyError
except KeyError as e:
print(f"キー {e} が存在しません")
try:
lst = [1, 2, 3]
print(lst[10]) # IndexError
except IndexError:
print("インデックスが範囲外です")
例外を発生させる(raise)
def divide(a, b):
if b == 0:
raise ValueError("0で割ることはできません")
return a / b
try:
result = divide(10, 0)
except ValueError as e:
print(e)
例外の再送出
try:
result = 10 / 0
except ZeroDivisionError:
print("ログに記録")
raise # 例外を再送出
独自例外の作成
class ValidationError(Exception):
"""入力値検証エラー"""
pass
class AgeValidationError(ValidationError):
"""年齢検証エラー"""
def __init__(self, age, message="年齢が不正です"):
self.age = age
self.message = message
super().__init__(self.message)
def validate_age(age):
if age < 0:
raise AgeValidationError(age, "年齢は0以上である必要があります")
if age > 150:
raise AgeValidationError(age, "年齢が大きすぎます")
return True
try:
validate_age(-5)
except AgeValidationError as e:
print(f"エラー: {e.message} (入力値: {e.age})")
コンテキストマネージャと例外
class DatabaseConnection:
def __enter__(self):
print("接続を開始")
return self
def __exit__(self, exc_type, exc_val, exc_tb):
print("接続を終了")
if exc_type is not None:
print(f"例外が発生: {exc_val}")
return False # 例外を再送出
with DatabaseConnection() as conn:
print("処理中...")
# raise ValueError("テストエラー")
ベストプラクティス
1. 具体的な例外をキャッチする
# 悪い例
try:
do_something()
except: # 全ての例外をキャッチ
pass
# 良い例
try:
do_something()
except SpecificError as e:
handle_error(e)
2. 例外を握りつぶさない
# 悪い例
try:
data = json.loads(text)
except:
data = {} # エラーを隠してしまう
# 良い例
try:
data = json.loads(text)
except json.JSONDecodeError as e:
logger.error(f"JSON解析エラー: {e}")
raise # または適切なデフォルト値を返す
3. EAFP vs LBYL
# LBYL (Look Before You Leap)
if key in dictionary:
value = dictionary[key]
else:
value = default
# EAFP (Easier to Ask Forgiveness than Permission)
# Pythonではこちらが推奨
try:
value = dictionary[key]
except KeyError:
value = default
# さらに良い方法
value = dictionary.get(key, default)
4. ログと例外の連携
import logging
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
def process_data(data):
try:
result = complex_operation(data)
return result
except ValueError as e:
logger.warning(f"値エラー: {e}")
return None
except Exception as e:
logger.exception("予期しないエラーが発生")
raise
実践例:APIクライアント
import requests
class APIError(Exception):
"""API関連のエラー"""
def __init__(self, status_code, message):
self.status_code = status_code
self.message = message
super().__init__(f"[{status_code}] {message}")
class APIClient:
def __init__(self, base_url):
self.base_url = base_url
def get(self, endpoint):
try:
response = requests.get(f"{self.base_url}{endpoint}")
response.raise_for_status()
return response.json()
except requests.exceptions.ConnectionError:
raise APIError(0, "接続できません")
except requests.exceptions.Timeout:
raise APIError(0, "タイムアウトしました")
except requests.exceptions.HTTPError as e:
raise APIError(e.response.status_code, str(e))
# 使用例
client = APIClient("https://api.example.com")
try:
data = client.get("/users")
except APIError as e:
print(f"APIエラー: {e}")
まとめ
try-exceptで例外をキャッチelseは例外が発生しなかった場合に実行finallyは必ず実行される(クリーンアップに使用)raiseで例外を発生させる- 具体的な例外をキャッチし、適切にログを取る
- 独自例外でエラーの種類を明確にする
次回はモジュールとパッケージについて学びます。