実際のコードでは「ファイルがない」「通信が失敗した」「データが想定と違う」など、避けようのないエラーが起きます。本記事では Python の例外処理の基本と、株価取得のような 失敗が前提 の処理での書き方を整理します。
目次
- 例外とは
- try / except の基本
- 例外の種類を明示する
- 例外オブジェクトを受け取る
- else と finally
- raise — 例外を発生させる
- ネットワーク処理での例外
- リトライの最小例
- やってはいけない書き方
例外とは
例外は「処理を続けられない異常な状況」を表すしくみです。例外が発生すると、その行で処理が止まり、上位に伝わっていきます。
prices = [2900, 2925, 2880]print(prices[10]) # IndexError: list index out of range例外を 捕まえる のが try / except です。
try / except の基本
prices = [2900, 2925, 2880]try: value = prices[10]except IndexError: value = Noneprint(value) # None構造は次のとおりです。
tryブロックには 失敗するかもしれない処理 を書くexcept 例外クラス:で どの種類の例外を捕まえるか を指定する- 捕まえた後の 代替処理 を except ブロックに書く
例外の種類を明示する
except: だけ書くと、すべての例外を捕まえてしまい、想定外のバグを覆い隠します。捕まえる例外は具体的に書く のが原則です。
import json
try: with open("config.json", "r", encoding="utf-8") as f: config = json.load(f)except FileNotFoundError: print("設定ファイルがないため、既定値を使います") config = {"window": 5}except json.JSONDecodeError: print("設定ファイルが壊れています") config = {"window": 5}複数の例外を 1 つの except でまとめて扱うこともできます。
try: ...except (FileNotFoundError, json.JSONDecodeError): config = {"window": 5}例外オブジェクトを受け取る
例外の詳細(メッセージなど)を使いたいときは as で変数に受け取ります。
try: value = int("abc")except ValueError as e: print(f"数値変換に失敗: {e}") # 数値変換に失敗: invalid literal for int() with base 10: 'abc'ログに残しておくと、後で原因を追いやすくなります。
else と finally
try / except には else と finally も書けます。
try: with open("stocks.csv", "r", encoding="utf-8") as f: text = f.read()except FileNotFoundError: print("ファイルがありません")else: print("読み込み成功:", len(text), "文字")finally: print("後処理を行います")| ブロック | いつ実行されるか |
|---|---|
else | 例外が発生しなかったとき |
finally | 例外の有無にかかわらず必ず実行される |
finally は「ファイルや接続を閉じる」「一時ファイルを消す」など、片付け処理に使います。
raise — 例外を発生させる
呼び出し元に「想定外の入力だった」と知らせたいときは、自分で例外を投げます。
def average(values: list[float]) -> float: if not values: raise ValueError("空のリストには平均が定義できません") return sum(values) / len(values)
try: average([])except ValueError as e: print(f"エラー: {e}")「使う側が分かっていれば防げる」エラーは、早めに raise で止めるほうが原因を特定しやすくなります。
ネットワーク処理での例外
株価取得などの API 呼び出しは、例外処理を 前提 に書きます。代表的な失敗パターンは次のとおりです。
| 状況 | 起きやすい例外 |
|---|---|
| 通信エラー | requests.ConnectionError / requests.Timeout |
| HTTP エラー | requests.HTTPError(404 / 500 など) |
| JSON が壊れている | json.JSONDecodeError |
import requests
def fetch_status(url: str) -> int | None: try: response = requests.get(url, timeout=10) response.raise_for_status() # 4xx / 5xx で HTTPError を発生 except requests.Timeout: print("タイムアウトしました") return None except requests.ConnectionError: print("接続できませんでした") return None except requests.HTTPError as e: print(f"HTTP エラー: {e}") return None return response.status_codetimeout を必ず指定するのが定石です。指定しないと、サーバ側の不具合のときにいつまでも待ってしまいます。
リトライの最小例
一時的な通信エラーは、少し待ってから再試行すると成功することがあります。
import timeimport requests
def fetch_with_retry(url: str, retries: int = 3, wait: float = 1.0) -> str | None: for attempt in range(retries): try: response = requests.get(url, timeout=10) response.raise_for_status() return response.text except (requests.Timeout, requests.ConnectionError): if attempt == retries - 1: return None time.sleep(wait * (2 ** attempt)) # 指数バックオフ return Noneループでリトライするときは、待機時間を徐々に伸ばす(指数バックオフ) のが負荷をかけにくい書き方です。
やってはいけない書き方
例外処理でやってはいけないパターンも覚えておきます。
# ❌ 何もしない except (エラーが消えてバグが見えなくなる)try: do_something()except Exception: pass
# ❌ Exception で全部捕まえる(本来の例外も握りつぶす)try: do_something()except Exception: print("失敗しました")「具体的な例外名で捕まえる」「捕まえた例外は最低限ログに残す」のが原則です。
生成AI へのプロンプト例
例外処理の追加を生成AI に依頼する例です。
次の Python 関数に、適切な例外処理を追加してください。
import requests
def fetch_prices(url: str) -> list[float]: response = requests.get(url) data = response.json() return [item["close"] for item in data]
要件:- 通信エラー / タイムアウト / HTTP エラー / JSON エラーを区別する- timeout=10 を指定する- 失敗時は空リストを返し、原因を print で出す- 過剰に except Exception で握りつぶさない- Python 3.12意図(失敗パターンと振る舞い)を明示すると、過不足のないコードが返ります(#7-2「データ分析のためのプロンプト設計」)。
まとめ
try / exceptで例外を捕まえる。例外の種類は具体的に書くelseは成功時、finallyは必ず実行されるブロックraiseで自分から例外を投げると、呼び出し側が原因を特定しやすい- ネットワーク処理では
timeoutとリトライをセットで考える