概要(「例外」は失敗を安全に扱うための仕組み)
例外は、処理が想定外の状態になったときに「止める/切り替える」ための信号です。Python では try/except/else/finally で「失敗時の動き」を明示し、必要なら raise で「意図的に失敗」を発生させます。ファイル操作は外部要因(存在しない、権限なし、使用中)で失敗しやすいため、代表例外の見分け方と「安全な受け止め方」を覚えると、一気に実務品質へ近づきます。
try:
with open("data.csv", "r", encoding="utf-8", newline="") as f:
print(f.readline())
except FileNotFoundError:
print("ファイルが見つかりません")
except PermissionError:
print("ファイルにアクセスできません(権限)")
Python例外処理の基本(ここが重要)
try/except/else/finally と raise の役割
try:
# 失敗しうる処理(例:ファイルI/O、パース、ネット)
...
except SomeError:
# 失敗時の代替(記録、再試行、メッセージなど)
...
else:
# 例外が起きなかったときだけ実行(後続処理)
...
finally:
# 成否に関係なく必ず実行(後片付け)
...
Python- 基本線: 失敗の可能性がある範囲を try に閉じ込め、起きうる例外を絞って except で受ける。
- 設計の肝: 例外ごとに「ユーザーへ見せるメッセージ」「ログの詳細」「再試行の有無」を決めておく。
- raise: 自作の検証に失敗した時など、意図的に例外を投げて上位へ判断を委ねる。
def must_positive(n: int) -> None:
if n <= 0:
raise ValueError("正の値が必要です")
Pythonファイル操作でよく出る例外の種類(実務頻度順の要点)
存在・パス・権限に関する例外
- FileNotFoundError: 指定パスが存在しない(ファイル/親ディレクトリ)。
対策の要点: 事前に親ディレクトリを作る、例外で「無ければスキップ」を許容。
from pathlib import Path
p = Path("out/report.txt")
p.parent.mkdir(parents=True, exist_ok=True)
with p.open("w", encoding="utf-8") as f:
f.write("ready\n")
Python- PermissionError: 権限不足(読めない・書けない・ロック中)。
対策の要点: ユーザーに権限確認を促す、別ディレクトリへの退避、リトライ設計。 - IsADirectoryError / NotADirectoryError: 期待と逆の種別(ファイルのはずがディレクトリ、またはその逆)。
対策の要点: os.path.isfile / is_dir のチェック、命名規約の徹底。
import os
path = "data"
if os.path.isdir(path):
print("これはディレクトリです")
Python- FileExistsError: 新規作成(”x” モードや mkdir)で既存がある。
対策の要点: 既存なら別名、タイムスタンプ付与、上書き方針を明確化。
try:
with open("report.txt", "x", encoding="utf-8") as f:
f.write("new\n")
except FileExistsError:
print("既に存在します")
Python文字コード・内容に関する例外
- UnicodeDecodeError / UnicodeEncodeError: エンコーディング不一致で文字化け(読めない/書けない)。
対策の要点: 原則 UTF-8 明示、unknown データは errors=”replace” で応急処置。
with open("unknown.txt", "r", encoding="utf-8", errors="replace") as f:
text = f.read() # 変換できない文字は置換
Python- JSONDecodeError(json.load 時): JSON の構文が壊れている/想定外。
対策の要点: 例外でファイル名と位置を記録、元データ修正を優先、フェイルセーフに切替。 - ValueError: パース・変換が失敗(例:int(“abc”))。
対策の要点: バリデーション実施、デフォルト値やスキップ設計。
OS・I/O 全般の例外
- OSError: OS 由来の広い失敗の親クラス(上記多くを包含)。
対策の要点: 詳細例外でできるだけ受ける(広すぎる except はバグ隠しになりがち)。 - IOError: 歴史的名称(現行は OSError 系)。古いコードで遭遇することあり。
try:
with open("dev.txt", "rb") as f:
f.read(1024)
except OSError as e:
print("OS系で失敗:", e)
Python深掘り(安全設計で守る・例外連鎖・TOCTOU)
事前準備と「後片付け」を with で自動化する
- 基本線: open は常に with、ディレクトリは事前に mkdir。
- 効果: 例外でも自動 close/リーク防止。中途半端な書き込みを減らす。
from pathlib import Path
p = Path("logs/app.log")
p.parent.mkdir(parents=True, exist_ok=True)
with p.open("a", encoding="utf-8", newline="\n") as f:
f.write("started\n")
PythonTOCTOU(存在確認→削除/移動の間に外部変更)の落とし穴
- 本質: os.path.exists の直後に他プロセスがファイルを動かすかも。
- 対策: 最終判断は操作(remove/rename)側の例外で行う。存在確認は“参考程度”。
import os
try:
os.remove("target.tmp") # 最終判断はここ
except FileNotFoundError:
pass # 既にないなら目的は達成
Python例外連鎖(原因を失わないメッセージ設計)
- ポイント: except で握りつつ、ログには元例外(repr(e))を残す。
- 効果: 調査・再現性が上がる。ユーザーには簡潔、ログは詳細で二層化。
実務のハンドリングパターン(準備・記録・復旧)
ユーザー向けメッセージと技術ログの分離
- ユーザー: 「ファイルが開けません。場所や権限を確認してください」
- ログ: 例外種類、スタック、パス、時刻、実行環境。
import logging
logging.basicConfig(filename="app.log", level=logging.INFO)
def load_text(path: str) -> str | None:
try:
with open(path, "r", encoding="utf-8") as f:
return f.read()
except (FileNotFoundError, PermissionError) as e:
logging.exception("load_text failed: %s", path)
return None
Pythonフェイルセーフ(安全側へ倒す)
- 読み取り失敗: スキップして続行、欠損として扱う。
- 書き込み失敗: テンポラリ保存→再試行、別ディレクトリへフォールバック。
import os
from pathlib import Path
def atomic_write(path: Path, data: str) -> bool:
tmp = path.with_suffix(path.suffix + ".tmp")
try:
tmp.write_text(data, encoding="utf-8")
os.replace(tmp, path)
return True
except OSError:
return False
Python例外を絞る(広すぎる except は避ける)
- 悪例: except Exception: で全て飲み込む(バグが隠れる)。
- 良例: 想定済みの具体例外だけを受け、未知は上位へ伝える。
例題で身につける(定番から一歩先まで)
例題1:読み込みの安全化(存在・権限・文字コード)
def read_text_safe(path: str) -> str | None:
try:
with open(path, "r", encoding="utf-8") as f:
return f.read()
except FileNotFoundError:
print("ファイルが存在しません:", path)
except PermissionError:
print("権限がありません:", path)
except UnicodeDecodeError:
print("文字コード不一致:", path)
return None
Python例題2:CSV の頑健な処理(壊れた行は記録して前進)
import csv
def total_qty(path: str) -> int:
total = 0
try:
with open(path, "r", encoding="utf-8", newline="") as f:
for i, row in enumerate(csv.reader(f), start=1):
try:
total += int(row[1])
except (IndexError, ValueError) as e:
print(f"WARN line={i} {e} row={row}")
except (FileNotFoundError, PermissionError) as e:
print("I/O 失敗:", e)
return total
Python例題3:新規のみ生成(誤上書き防止)
def create_once(path: str, content: str) -> bool:
try:
with open(path, "x", encoding="utf-8", newline="\n") as f:
f.write(content)
return True
except FileExistsError:
print("既存のため作成しません:", path)
return False
Python例題4:JSON 読み(壊れた構文を検知してフェイルセーフ)
import json
def load_json_safe(path: str) -> dict | list | None:
try:
with open(path, "r", encoding="utf-8") as f:
return json.load(f)
except FileNotFoundError:
print("ファイルが見つかりません:", path)
except PermissionError:
print("権限がありません:", path)
except json.JSONDecodeError as e:
print("JSON構文エラー:", e)
return None
Pythonまとめ
例外は「外部の不確実性」を安全に受け止めるための仕組みです。ファイル操作では、存在(FileNotFoundError)、権限(PermissionError)、種別不一致(IsADirectoryError/NotADirectoryError)、衝突(FileExistsError)、文字コード(UnicodeDecodeError)を軸に考え、try/except を「必要十分な範囲」で掛けるのが基本線。with で後片付けを自動化し、TOCTOU を例外ベースで回避、ユーザー向けメッセージと技術ログを分離する。未知の失敗は握り過ぎずに上位へ伝え、フェイルセーフで運用を止めない。これらを習慣化すれば、初心者でも例外に強い、堅牢なファイル処理を書けます。
