Pythonの基礎知識(基礎編)

【Pythonの基礎知識】例外処理で“壊れない仕組み”を設計する

プログラムを書いていると、想定外の事態が必ず発生します。

たとえば入力値が不正だったり、外部ファイルが見つからなかったり、ネットワークが途切れたり。

これらを放置すると、仕組みが突然停止してユーザーに迷惑をかけたり、ログが散らかって原因把握ができなくなったりします。

そこで、Pythonにおける例外処理は、単なる「エラー回避」ではなく「壊れない仕組みを設計するための第一歩」です。

公式ドキュメントでも、try/except/else/finally を適切に組み合わせることが推奨されています。

本記事では、実体験を交えながら「例外処理を設計する視点」「心理的な安心を設ける設計」を掘り下げ、壊れにくい仕組みづくりに踏み込んでいきます。

Pythonの基礎知識(基礎編)


🟣 Pythonの基礎知識(基礎編)
📌基本文法から実用テクニックまで、Pythonの土台を固めるステップアップ講座
└─ 【Pythonの基礎知識(基礎編)】仕組みから学ぶ思考と自動化のプログラミング講座
  ├─ STEP 0:Pythonを動かす“仕組み”を理解する
  | ├─【Pythonの基礎知識】Pythonを動かす環境とは何か? “自分専用の環境”を作る
  | ├─【Pythonの基礎知識】Hello Worldの裏側にある実行の仕組み
  | └─【Pythonの基礎知識】Pythonのファイル構造と実行パスを理解する
  |
  ├─STEP 1:Pythonで“考える仕組み”を作る(思考編)
  | ├─【Pythonの基礎知識】データ型で世界を定義する|数・文字・真偽の正体
  | ├─【Pythonの基礎知識】変数と値の動きを通して仕組みを理解しよう
  | ├─【Pythonの基礎知識】条件分岐で“判断を任せる”仕組みを作る
  | ├─【Pythonの基礎知識】for文で“人の手”を離す仕組みを作る
  | └─【Pythonの基礎知識】while文で“継続する仕組み”を作る
  |
  ├─STEP 2:Pythonで“情報を扱う仕組み”を作る(構造編)
  | ├─【Pythonの基礎知識】コレクション型の正しい選び方(list, tuple, dict, set)
  | ├─【Pythonの基礎知識】リストで情報を整理し、仕組みに流れを持たせる
  | ├─【Pythonの基礎知識】辞書型でデータを“意味”で管理する
  | └─【Pythonの基礎知識】集合型で重複を排除し、無駄をなくす仕組みを作る
  |
  ├─STEP 3:Pythonで“動きを再利用する仕組み”を作る(関数・モジュール編)
  | ├─【Pythonの基礎知識】関数で処理を再利用する|“人間の手順”を仕組みに変える
  | ├─【Pythonの基礎知識】引数と戻り値で“情報のやりとり”を自動化する
  | ├─【Pythonの基礎知識】モジュールとパッケージで“仕組みを部品化”する
  | └─【Pythonの基礎知識】importの裏側を理解し、コードを分離する設計思考
  |
  ├─STEP 4:Pythonで“データを扱う仕組み”を作る(入出力・永続化編)
  | ├─【Pythonの基礎知識】ファイル操作でデータを読み書きする仕組みを作る
  | ├─【Pythonの基礎知識】CSVを自在に扱う仕組みを作る
  | ├─【Pythonの基礎知識】JSONで構造化データを操る
  | └─【Pythonの基礎知識】例外処理で“壊れない仕組み”を設計する
  |
  └─STEP 5:Pythonで“自動化する仕組み”を作る(応用実践編)
    ├─【Pythonの基礎知識】スクリプトを自動実行させる仕組みを作る
    ├─【Pythonの基礎知識】日次タスクを自動化して人の時間を解放する
    ├─【Pythonの基礎知識】外部APIを活用して作業を外部化する
    └─【Pythonの基礎知識】ログを記録して仕組みの信頼性を高める

例外処理の役割と設計視点

プログラムが動く以上、予期せぬエラーは必ず発生します。

ファイルが見つからない、ネットワークが切断される、数値計算でゼロ除算が起こるなど、原因は多様です。

そんなときに処理が止まってしまうと、システム全体の信頼性が揺らぎます。

Pythonの例外処理は、まさに「壊れない仕組み」を支える重要な要素です。

ここでは、例外処理の本質と設計の考え方を整理していきます。

Pythonの例外とは?

Pythonの例外とは、プログラムが想定していない状態になったときに発生する「異常信号」のようなものです。

プログラムはその時点で通常の処理を続けられないと判断し、例外を発生させます。

これは「止めるため」ではなく、「安全に立て直すため」の仕組みです。

Pythonの例外処理は「壊れない仕組み」を支える設計思想であり、単なるエラー対策ではありません。

異常が起きてもプログラムが途中で止まらず、ユーザーやシステムに悪影響を与えないように設計するための構造です。

つまり、例外は“プログラムの失敗”ではなく“修正のチャンス”なんですね?
その通りです。例外は悪ではなく、異常を発見したことを知らせる合図です。開発者が正しく受け止めて対処すれば、システム全体の信頼性を保つことができます。

tryとexceptを理解するだけでなく、設計段階で「どんな失敗を想定し、どう動かすか」をあらかじめ決めておくことが、信頼性の高いプログラムを作る鍵になります。

たとえば、ファイル読み込みやネットワーク通信、ユーザー入力などは必ず例外を想定しておく必要があります。

try:
    num = int(input("数字を入力してください: "))
except ValueError:
    print("数字以外が入力されました。もう一度試してください。")

【出力例:】

数字以外が入力されました。もう一度試してください。

このように、例外を補足してユーザーにわかりやすいメッセージを返すことで、プログラムは落ちずに安全に処理を継続できます。

例外処理は単なる防御ではなく、ユーザー体験を守る“設計の要”なのです。

例外処理が必要となる典型的な場面

Pythonでは、エラーが発生すると「例外(Exception)」が投げられます。

例えばファイルの読み込みや数値変換など、外部要因や入力値次第で結果が変わる処理は特に注意が必要です。

例外処理を設計に組み込むことで、異常が起きても安全に処理を継続できるようになります。

ファイルを開く処理って、普通に書けば動くのに、わざわざ例外処理が必要なんですか?
そうですね。正常時は動きますが、ファイルが存在しないケースではプログラムが即停止します。現場では想定外のファイル名やパスミスが日常的に起こるため、例外処理が保険になります。

たとえば、ファイルが存在しない場合の基本的な例は次の通りです。

try:
    with open("data.txt", "r") as f:
        content = f.read()
except FileNotFoundError:
    print("ファイルが見つかりません。")

【出力例:】

ファイルが見つかりません。

このように例外を補足することで、ユーザーにエラーメッセージを返しながらもプログラム全体は正常に動作し続けます。

「壊れない仕組み」を目指す設計の観点

例外処理を設計する際のポイントは「想定できる失敗をすべて洗い出す」ことです。

これを怠ると、隠れたバグがシステムを止める原因になります。

例外を単に握りつぶすのではなく、意味をもって処理を分岐させることが重要です。

たとえば、通信エラーでリトライを行うか、ユーザー入力ミスを通知して再入力を促すかなど、失敗の性質によって適切な行動を定義します。

例外処理って書けば安全になると思ってました。

実は設計段階から考えるものなんですね。

その通りです。例外処理は“エラー対応の設計書”のようなものです。

どんな異常を想定して、どう復旧するのかを明文化しておくと、後からの保守も楽になります。

設計段階で意識すべき主な項目を次の表にまとめます。

観点設計内容
例外の種類どのようなエラーが発生し得るかを明確化
処理の優先度継続・停止・リトライなどの判断基準を設定
ログ出力再発防止や調査のために必ず記録を残す
ユーザー通知ユーザーが理解できる形で異常を伝える

システムが壊れないというのは、単にエラーを出さないことではありません。

異常が発生しても動き続ける柔軟性を持つことが本質です。

例外処理を「最後の防波堤」と捉えず、「設計思想の一部」として組み込むことが、堅牢なPythonプログラムを作る第一歩です。

基本構文と公式ドキュメントに基づく使い方

Pythonの例外処理は、「何か問題が起きたときに安全に処理を続ける」ための仕組みです。

単にエラーを防ぐためではなく、システムを止めずに復旧させるための手段です。

Python公式ドキュメントでは、try/except/else/finallyの4つの構文を使い分けて例外を制御する方法が示されています。

ここではそれぞれの意味と、実務での使い方を具体的に見ていきます。

try/except/else/finally構文の概要と使い分け

Pythonの例外処理構文は、tryを中心にして設計されています。

tryの中に“エラーが起きそうな処理”を書き、exceptで“異常時の対応”を定義します。

elseは“問題がなかった場合”の処理、finallyは“結果に関わらず必ず実行したい処理”を記述します。

tryとexceptはわかるんですが、elseとfinallyの使い分けがよく分からないです。
いい質問です。elseは“正常に終わったときだけ動く”ブロックで、finallyは“エラーの有無に関係なく最後に動く”ブロックです。つまり、処理の流れをきれいに分けて管理できるのがPythonの例外構文の特徴なんです。

次の例は、ファイル読み込み時の例外処理を含めた基本構文です。

try:
    with open("sample.txt", "r") as f:
        data = f.read()
except FileNotFoundError:
    print("ファイルが見つかりません。")
else:
    print("ファイルの読み込みが完了しました。")
finally:
    print("処理を終了します。")

【出力例:】

ファイルが見つかりません。
処理を終了します。

この例では、ファイルが存在しない場合にFileNotFoundErrorが発生し、exceptブロックが実行されます。

ファイルが正常に読み込めたときはelseの内容が出力され、最後に必ずfinallyが実行されます。

表でまとめると以下のようになります。

構文実行タイミング主な用途
try例外が発生する可能性のある処理を囲む監視対象のメイン処理
except例外発生時に実行されるエラー対処、ログ出力など
else例外が発生しなかった場合に実行される成功時の後処理
finally例外の有無に関係なく実行されるリソース開放、終了処理

例外処理をこうして構造的に書くことで、処理の流れが明確になり、バグ調査もしやすくなります。

「raise」でエラーを“自分で発生させる”

例外処理は「起きたエラーを受け止める」だけではありません。

ときには、開発者が「この条件は異常だ」と判断したときに自分で例外を起こすこともあります。

そのときに使うのが raise です。

例外って勝手に起きるものじゃないんですか?
いいえ、開発者が“意図的に起こす”こともあります。入力値が不正だったり、処理をこれ以上続けると危険だと判断したときに、raiseを使って上位に知らせるんです。

def validate_age(age):
    if age < 0:
        raise ValueError("年齢がマイナスです。")
    return "登録完了"

【出力例:】

ValueError: 年齢がマイナスです。

raise は「異常を知らせる警報ボタン」です。

try/exceptが「受け止める側」なら、raiseは「投げる側」です。

この2つを組み合わせることで、プログラムは“自分で問題を検知し、自分で安全に止まる”仕組みを作れるのです。

組み込み例外クラスの理解とカスタム例外設計

Pythonには、すでに多くの「組み込み例外クラス」が用意されています。

これを理解せずに例外を無理やりexceptで捕まえようとすると、原因の特定が難しくなります。

どんなエラーでもexcept Exceptionでまとめてキャッチしておけば安心じゃないんですか?
それだと「どんなエラーが起きたのか」が分からなくなります。PythonにはFileNotFoundError、ValueError、ZeroDivisionErrorなど目的別の例外クラスがあり、これを正しく使うことで原因が一目で分かるようになります。

例えば、ゼロ除算を検知する場合は次のように記述します。

try:
    result = 10 / 0
except ZeroDivisionError:
    print("ゼロでは割り算できません。")

【出力例:】

ゼロでは割り算できません。

このように、特定のエラーに応じたexceptを使えば、バグの発生箇所をすぐに突き止めることができます。

さらに、実務では独自の業務ロジックを持つ「カスタム例外」を設計することもあります。

たとえばデータ検証のルール違反を検出する場合などです。

class ValidationError(Exception):
    pass

def validate_data(value):
    if not value.isdigit():
        raise ValidationError("数値以外が入力されました。")

try:
    validate_data("abc")
except ValidationError as e:
    print(e)

Pythonの pass は、「何もしない命令」です。Shellでいうところの「:(コロン)」とほぼ同じ役割を持ちます。もう少し正確に言うと、pass は構文上なにか書く必要があるけれど、特に実行したい処理がないときに使う占位文です。たとえば、class や def の中は空のままだと文法エラーになるため、最低1行の命令(この場合は pass)を置くことで構文を成立させています。

ポイント

  • class ValidationError(Exception):
    → Exceptionクラスを継承して新しい例外クラスを定義。
  • pass → このクラスでは特に新しい処理を追加しないので、空のままにする。
    (将来的に拡張したいときはここに処理を追加できる)

【出力例:】

数値以外が入力されました。

このように自作の例外クラスを定義すると、業務ロジックの中で“想定済みの異常”を明示的に扱えるようになります。

これはトラブル時のログ分析や保守性の向上にも大きく貢献します。

例外処理は「安全に止まるための技術」ではなく、「安全に動き続けるための設計」です。

構文の理解だけで終わらせず、発生する異常をどう受け止めるかまで考えて設計することが、堅牢なPythonシステムを作る最短ルートです。

大業的なPythonの例外

代表的なPythonの例外(Exception)クラスを、用途ごとに整理して一覧にしました。

公式ドキュメント(Python 3.12時点)に基づく代表的なものを抜粋しています。

カテゴリ例外クラス名主な発生原因・用途
基本例外Exceptionすべてのユーザー定義例外の基底クラス
構文系SyntaxError文法エラー(コードが不正)
構文系IndentationErrorインデント(字下げ)の不整合
実行時NameError未定義の変数・関数を参照
実行時TypeError異なる型同士の演算や操作
実行時ValueError値が不正な場合(例:int("abc"))
算術演算ZeroDivisionError0で割り算を行った場合
ファイル・IOFileNotFoundError存在しないファイルを開こうとした
ファイル・IOPermissionErrorアクセス権のないファイル操作
ファイル・IOIOError入出力処理全般の失敗(旧式)
インデックスIndexErrorリスト・タプルなどで範囲外アクセス
キー操作KeyError辞書に存在しないキーを指定
モジュールImportErrorモジュールの読み込みに失敗
モジュールModuleNotFoundError指定モジュールが存在しない
属性参照AttributeError存在しない属性やメソッドを呼び出し
システム系OSErrorOS関連のエラー全般(IOErrorを内包)
システム系MemoryErrorメモリ不足で処理が継続できない
システム系RuntimeErrorその他の実行時エラー
ネットワークConnectionError通信接続に関する基底例外
ネットワークTimeoutError処理が規定時間内に終わらない
ユーザー入力EOFError入力待ち状態でEOFを受け取った
ユーザー定義CustomError(例:ValidationError)業務ロジック専用に定義する例外

Pythonの例外体系はツリー構造になっており、BaseException → Exception → 個別例外の順に継承されています。

通常は Exception を継承して自作の例外クラスを作成します。

 実務でよく使うのは以下の7種類です:

代表的なException

  • FileNotFoundError
  • ValueError
  • TypeError
  • KeyError
  • IndexError
  • ZeroDivisionError
  • AttributeError

これらを理解して使い分けるだけで、トラブル時の原因特定スピードが大幅に上がります。

実践ケース:自分のプロジェクトで起きた異常と対策

どんなに設計を丁寧にしても、実際の現場では想定外のトラブルが起きます。

ここでは筆者が実際に経験した「ファイル読み込みエラー」を題材に、どのように例外処理を設計し直したのかを紹介します。

コード上の知識だけでなく、現場での思考プロセスを学ぶことで、より実践的な判断ができるようになります。

実体験:ファイル読み込みにおける例外発生と対応

ある案件で、ログ解析用のCSVを自動集計するスクリプトを運用していました。

ところが、ある朝バッチ処理が途中で停止していることに気づいたのです。

原因は単純で、「指定したファイルが存在しない」ことでした。

そんな単純なミスで止まるんですか?

Pythonならスキップしてくれそうですけど。

Pythonは融通が効くように見えますが、存在しないファイルを開こうとすればFileNotFoundErrorが発生して即停止します。例外処理を入れておかないと、バッチ処理全体が止まってしまうんです。

修正後は次のようにtry-except構文を導入しました。

import os

file_path = "logs/2025-11-01.csv"

try:
    if not os.path.exists(file_path):
        raise FileNotFoundError("指定したログファイルが存在しません。")
    with open(file_path, "r") as f:
        data = f.read()
    print("読み込み成功:", len(data), "バイト")
except FileNotFoundError as e:
    print("ERROR:", e)

【出力例:】

ERROR: 指定したログファイルが存在しません。

このように例外処理を追加しておけば、スクリプトが途中で止まらず、次の処理へ安全に移行できます。

さらに、ログ出力を組み合わせることで、異常発生時の追跡も容易になります。

対応設計から得られた気づきと今後の改善ポイント

この経験を通して分かったのは、例外処理の目的は「止めない」だけではなく「原因をすぐに特定できる形にする」ことです。

つまり、例外を握りつぶすのではなく、正確に記録して再現できる状態を残すことが重要です。

確かに、止まらないだけじゃ次に何が起きたのか分かりませんね。どうすればいいんですか?
ログ設計と組み合わせるのが効果的です。異常が起きたら、いつ・どこで・何が発生したかをファイルに出力しておけば、次回の原因究明が一瞬で終わります。

ログの書き出しを追加した例がこちらです。

import datetime

def log_error(message):
    with open("error.log", "a") as log:
        timestamp = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
        log.write(f"[{timestamp}] {message}\n")

try:
    raise ValueError("データ形式が不正です。")
except ValueError as e:
    log_error(str(e))
    print("処理を継続します。")

【出力例:】

処理を継続します。

error.logファイルには次のように記録されます。

[2025-11-03 10:32:12] データ形式が不正です。

このような設計にしておくと、現場でトラブルが起きても開発チーム全員が迅速に対応できます。

改善項目実装効果
例外の補足プログラムの停止防止
ログ出力の追加原因追跡の迅速化
カスタムメッセージユーザーに具体的な指示を提示

例外処理は、単なる防御策ではなく「事故が起きてもシステムを立て直すための知恵」です。

実際のトラブルから得た知見を設計に反映させることで、コードは“動くだけの仕組み”から“持続する仕組み”へと進化していきます。

まとめ

例外処理は、プログラムを“止めない”ための技術であり、同時に“信頼性を設計する”ための考え方でもあります。

try/exceptで異常を補足する仕組みを作り、else/finallyで処理を整理しておけば、どんなエラーが起きても慌てずに制御できます。

結局、例外処理って本当に必要なんですか?

簡単なスクリプトなら省略しても問題なさそうですけど。

短期的には動きますが、長期的には必ず破綻します。例外処理を入れておくことで「壊れたときにどうするか」を最初から決めておけるので、後々の修正コストが格段に下がるんです。

これまで見てきたように、例外処理は「安全に止める」ためではなく、「安全に動かし続ける」ための仕組みです。

実務ではこの思想をコード全体に広げることで、予期せぬ事態に強いシステムへと育てることができます。

最後に、例外処理を設計に組み込む際の要点を整理します。

設計ポイント目的
発生しうる異常を洗い出す予期せぬ停止を防ぐ
例外クラスを明確に使い分ける原因の特定を容易にする
ログ出力とセットで設計する再発防止と調査時間の短縮
ユーザーに意味のある通知を返す操作エラー時の混乱を防ぐ

Pythonの例外処理は、単なるエラー回避の構文ではなく「設計思想そのもの」です。

これを理解し、自分のプロジェクトに適用できるようになると、コードは“動くだけのスクリプト”から“守れる仕組み”へと変わります。

次のおすすめ記事

よく読まれている記事

1

「私たちが日々利用しているスマートフォンやインターネット、そしてスーパーコンピュータやクラウドサービス――これらの多くがLinuxの力で動いていることをご存じですか? 無料で使えるだけでなく、高い柔軟 ...

2

Linux環境でよく目にする「Vim」という名前。サーバーにログインしたら突然Vimが開いてしまい、「どうやって入力するの?」「保存や終了ができない!」と困った経験をした人も多いのではないでしょうか。 ...

3

ネットワーク技術は現代のITインフラにおいて不可欠な要素となっています。しかし、ネットワークを深く理解するためには、その基本となる「プロトコル」と「レイヤ」の概念をしっかり把握することが重要です。 こ ...

4

この記事は、Linuxについて勉強している初心者の方向けに「Shellスクリプト」について解説します。最後まで読んで頂けましたら、Shellスクリプトはどのような役割を担っているのか?を理解出来るよう ...

5

Javaは世界中で広く使われているプログラミング言語であり、特に業務システムやWebアプリケーションの開発において欠かせない存在です。本記事では、初心者向けにJavaの基礎知識を網羅し、環境構築から基 ...

-Pythonの基礎知識(基礎編)