実際のコードでは「ファイルがない」「通信が失敗した」「データが想定と違う」など、避けようのないエラーが起きます。本記事では Python の例外処理の基本と、株価取得のような 失敗が前提 の処理での書き方を整理します。

目次

  1. 例外とは
  2. try / except の基本
  3. 例外の種類を明示する
  4. 例外オブジェクトを受け取る
  5. else と finally
  6. raise — 例外を発生させる
  7. ネットワーク処理での例外
  8. リトライの最小例
  9. やってはいけない書き方

例外とは

例外は「処理を続けられない異常な状況」を表すしくみです。例外が発生すると、その行で処理が止まり、上位に伝わっていきます。

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 = None
print(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 には elsefinally も書けます。

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_code

timeout を必ず指定するのが定石です。指定しないと、サーバ側の不具合のときにいつまでも待ってしまいます。

リトライの最小例

一時的な通信エラーは、少し待ってから再試行すると成功することがあります。

import time
import 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 とリトライをセットで考える