閉じる

Pythonのtry-exceptでエラーでも止まらない処理の書き方を解説

外部ファイルの欠如やゼロ除算など、ちょっとした出来事でプログラムは簡単に止まってしまいます。

エラーでも止まらないようにするにはtry-exceptが鍵です。

この記事ではPython初心者の方に向けて、基本の書き方から安全に継続するパターンまでを、動くサンプルと出力つきで丁寧に解説します。

Pythonのtry-exceptの基本

例外処理でプログラムを止めない仕組み

Pythonでは、実行時に問題が起きると「例外」と呼ばれるイベントが発生し、何もしないとプログラムはそこで停止します。

tryで「エラーが出そうな処理」を囲み、対応するexceptで受け止めることで、停止せずに先へ進めます

例外を捕まえると、そのブロック内で代替処理や記録を行い、残りの処理へ継続できます。

Python
# 例: ゼロ除算を捕まえて、エラーメッセージを出してから続行する
print("開始")

try:
    x = 1 / 0  # ここで ZeroDivisionError が発生
    print("この行は実行されません")
except ZeroDivisionError as e:
    # 例外オブジェクト e からメッセージを確認しつつ継続
    print(f"エラー発生: {type(e).__name__}: {e} -> でも続行します")

print("終了")
実行結果
開始
エラー発生: ZeroDivisionError: division by zero -> でも続行します
終了

「止まる」代わりに「受け止めて続ける」ことが、例外処理の本質です

この流れを体感しておくと、以降の具体例の理解がスムーズになります。

最小の書き方(tryとexceptだけ)

最小構成はtryexceptだけです。

学習段階ではexcept Exception as eで「とにかく止めない」を体験できます

ただし運用では後述の注意点に従いましょう。

Python
# 最小の例: まずは「止めない」を体験
def risky():
    # ここではわざとエラーを起こします
    raise ValueError("サンプルのエラー")

print("処理開始")
try:
    risky()
except Exception as e:  # 学習用: 例外を広く受ける
    print(f"止めずに続行: {type(e).__name__} -> {e}")
print("処理終了")
実行結果
処理開始
止めずに続行: ValueError -> サンプルのエラー
処理終了

注意: 実務では「なんでもException」で受けるのは非推奨です。

下の「安全に使うコツ」で理由と改善策を説明します。

最小の形は学習・試作のためにとどめ、本番では例外を絞り込みます

どこに置くか(エラーが出そうな行を囲む)

tryの範囲は「エラーが出そうな最小の行」だけを囲むのが基本です。

範囲が広いと「どこで失敗したか」が分かりにくくなります。

Python
# 悪い例: tryの範囲が広すぎて、どこで失敗したか分かりづらい
def step1():
    print("step1 OK")

def step2():
    print("step2 実行")
    raise RuntimeError("step2で失敗")

def step3():
    print("step3 OK")

try:
    step1()
    step2()  # ここで失敗する
    step3()  # ここまで来ない
except Exception as e:
    print(f"どこで失敗したか特定しづらい: {type(e).__name__}: {e}")
実行結果
step1 OK
step2 実行
どこで失敗したか特定しづらい: RuntimeError: step2で失敗
Python
# 良い例: 失敗が想定される箇所だけを最小で囲む
def step1():
    print("step1 OK")

def step2():
    print("step2 実行")
    raise RuntimeError("step2で失敗")

def step3():
    print("step3 OK")

step1()
try:
    step2()  # ここだけを最小で囲む
except RuntimeError as e:
    print(f"step2だけ捕捉: {type(e).__name__}: {e}")
step3()
実行結果
step1 OK
step2 実行
step2だけ捕捉: RuntimeError: step2で失敗
step3 OK

「どの行で何が起きたか」を切り分けられるように、tryは最小の範囲で設計します

すぐ使える書き方とサンプル

代表的なエラーと、止めずに続けるための基本方針を次の表に整理します

ここでの方針はあくまで一例であり、実際には要件に合わせて調整してください。

例外代表的な原因継続の方針(例)
ZeroDivisionError0で割る0やNoneなどのデフォルト値を返す
FileNotFoundErrorファイルが存在しない空ファイルを作る/スキップする
KeyError辞書にキーがないデフォルト値で代用する
IndexErrorリストの範囲外アクセス該当要素をスキップする

以下で各ケースを実行可能なサンプルで確認します

ゼロ除算(ZeroDivisionError)を0で代用

割り算の分母が0になる可能性があるなら、例外を捕まえて0を返すことで、集計などを止めずに続けられます

Python
# ゼロ除算を0で代用する安全な割り算
def safe_div(num, den):
    try:
        return num / den
    except ZeroDivisionError as e:
        print(f"[警告] {type(e).__name__}: {e} -> 0で代用します")
        return 0  # デフォルト値で継続

# 動作確認
print(safe_div(10, 2))   # 5.0
print(safe_div(10, 0))   # 0 で代用
print(safe_div(0, 10))   # 0.0
実行結果
5.0
[警告] ZeroDivisionError: division by zero -> 0で代用します
0
0.0

「エラーでも結果を返す」ことで後続の処理が止まらず、全体の安定性が上がります

ただし0での代用が妥当かは要件に合わせて判断してください。

ファイルがない(FileNotFoundError)時は作成かスキップ

ファイル読み込みは外部要因に左右されやすいです。

存在しない場合に「作成して続行」または「スキップして続行」の2パターンが現実的です

Python
# パターンA: なければ作成してから読み込む
from pathlib import Path

path = Path("sample_data.txt")

try:
    text = path.read_text(encoding="utf-8")
except FileNotFoundError as e:
    print(f"[情報] {type(e).__name__}: {e} -> 新規作成します")
    path.write_text("初期データ\n1行目\n", encoding="utf-8")
    text = path.read_text(encoding="utf-8")

print("読み込んだ内容:")
print(text)
実行結果
[情報] FileNotFoundError: [Errno 2] No such file or directory: 'sample_data.txt' -> 新規作成します
読み込んだ内容:
初期データ
1行目
Python
# パターンB: なければスキップして他の処理を続ける
from pathlib import Path

def process_optional_file(p: Path):
    try:
        print("任意ファイルの1行目:", p.read_text(encoding="utf-8").splitlines()[0])
    except FileNotFoundError as e:
        print(f"[注意] {type(e).__name__}: {e} -> このファイルはスキップします")

process_optional_file(Path("optional_missing.txt"))
print("スキップ後も他の処理を継続できます")
実行結果
[注意] FileNotFoundError: [Errno 2] No such file or directory: 'optional_missing.txt' -> このファイルはスキップします
スキップ後も他の処理を継続できます

「足りなければ作る」「重要度が低ければ飛ばす」といった判断を明確化すると、設計が安定します

辞書キーがない(KeyError)はデフォルト値で続行

辞書アクセスはキー欠落でKeyErrorになります。

try-exceptで捕まえてデフォルト値をあてれば、安全に前へ進めます

Python
# プロファイルから年齢を取得。なければ "unknown" で続行
profile = {"name": "Taro"}  # "age" が無い

try:
    age = profile["age"]
except KeyError as e:
    print(f"[注意] {type(e).__name__}: {e} -> デフォルト値で続行")
    age = "unknown"

print(f"name={profile['name']}, age={age}")
実行結果
[注意] KeyError: 'age' -> デフォルト値で続行
name=Taro, age=unknown

補足: 辞書はdict.get("age", "unknown")で例外なくデフォルト値を返せます。

ただし、「なぜ無いのか」をログに残したい場合は、あえてtry-exceptでメッセージを出す設計も有効です。

リスト範囲外(IndexError)は処理をスキップ

無効な添字アクセスはスキップして、処理を続けます

Python
data = ["A", "B", "C"]
indices = [0, 2, 5, 1]  # 5 は範囲外

for idx in indices:
    try:
        item = data[idx]
        print(f"index={idx} -> {item}")
    except IndexError as e:
        print(f"[注意] {type(e).__name__}: {e} -> index={idx} はスキップ")
実行結果
index=0 -> A
index=2 -> C
[注意] IndexError: list index out of range -> index=5 はスキップ
index=1 -> B

「不正な要素だけ飛ばす」ことで、全体の処理を妨げずに結果を得られます

安全に使うコツ(初心者向け)

exceptを広げすぎない(Exceptionの丸取りは避ける)

なんでもexcept Exceptionで受けると、想定外のバグまで隠してしまいます

具体的な例外名で絞るのが基本です。

Python
# 悪い例: 広く受けて握りつぶす -> 問題の特定が困難に
try:
    1 / 0
except Exception:
    pass  # 何も出さないのは避ける
Python
# 良い例: 具体的な例外だけを捕まえ、状況を明示する
try:
    1 / 0
except ZeroDivisionError as e:
    print(f"ゼロ除算でした: {e}")
実行結果
ゼロ除算でした: division by zero

複数の想定があるならexcept (TypeError, ValueError) as e:のようにタプルで列挙します

エラーを握りつぶさない(原因を表示)

少なくとも「型名」と「メッセージ」を表示・記録しましょう。

これだけで原因追跡が大きく前進します。

Python
# 型名とメッセージを出してから継続する
def to_int(s):
    try:
        return int(s)
    except ValueError as e:
        print(f"[変換失敗] {type(e).__name__}: {e} (入力: {s!r}) -> 0で代用")
        return 0

print(to_int("42"))
print(to_int("abc"))  # 失敗するが止まらない
実行結果
42
[変換失敗] ValueError: invalid literal for int() with base 10: 'abc' (入力: 'abc') -> 0で代用
0

開発中は詳細を出し、本番では必要十分な情報だけに調整するのが現実的です

例外オブジェクト(e)のメッセージを確認

例外オブジェクトeには、原因を示す文字列や属性が入っています

最低限、型名とメッセージを見ましょう。

Python
try:
    result = {}["missing"]
except KeyError as e:
    print("型名:", type(e).__name__)
    print("メッセージ:", str(e))
実行結果
型名: KeyError
メッセージ: 'missing'

型名はtype(e).__name__、テキストはstr(e)で確認できます

tryの範囲は最小にする

tryブロックは「落ちる可能性がある行」だけを囲み、前後の安全な処理は外に出します

これにより、デバッグと保守が容易になります。

Python
# 悪い例: まとめて囲んでしまう
try:
    raw = "123"
    n = int(raw)        # ここが失敗しうる
    doubled = n * 2
    print(doubled)
except ValueError as e:
    print("どこで失敗したか切り分けにくい:", e)

# 良い例: 失敗しうる箇所だけ最小で囲む
raw = "123"
try:
    n = int(raw)
except ValueError as e:
    print("数値化失敗:", e)
    n = 0  # デフォルト
doubled = n * 2
print(doubled)
実行結果
246

「最小で囲む」だけで、可読性も信頼性も大きく向上します

継続のためのパターン

デフォルト値で処理を続ける

失敗時にデフォルト値へフォールバックして、全体の処理を継続するのは定番です。

Python
# 文字列を数値に変換しつつ合計。失敗したら 0 と見なす
values = ["10", "20", "x", "30", "y"]

total = 0
for s in values:
    try:
        total += int(s)
    except ValueError as e:
        print(f"[注意] {type(e).__name__}: {e} -> '{s}' を 0 とみなして続行")

print("合計:", total)
実行結果
[注意] ValueError: invalid literal for int() with base 10: 'x' -> 'x' を 0 とみなして続行
[注意] ValueError: invalid literal for int() with base 10: 'y' -> 'y' を 0 とみなして続行
合計: 60

入力の一部が壊れていても、全体の出力を得られるのがメリットです

簡単なリトライ(回数を決める)

一時的な失敗(ネットワークなど)は一定回数のリトライで回避できます。

ここでは再現性のため、1回目は失敗、2回目で成功する関数を擬似的に作ります。

Python
# 1回目は失敗、2回目で成功するダミー関数
attempt_state = {"count": 0}

def flaky_operation():
    attempt_state["count"] += 1
    if attempt_state["count"] < 2:
        raise ConnectionError("一時的な接続エラー")
    return "OK"  # 2回目で成功

max_retries = 3
for attempt in range(1, max_retries + 1):
    try:
        result = flaky_operation()
        print(f"成功: {result} (試行{attempt}回目)")
        break
    except ConnectionError as e:
        print(f"失敗(試行{attempt}回目): {e}")
        if attempt == max_retries:
            print("これ以上は試行せずに続行(または後で再実行)")
実行結果
失敗(試行1回目): 一時的な接続エラー
成功: OK (試行2回目)

回数や待機時間(指数バックオフなど)は要件に応じて調整し、無限リトライは避けます

失敗したデータをスキップして次へ進む

1件の失敗で全体を止めず、「失敗したものだけ」別扱いにして前へ進むのも有効です。

Python
# 一部に不正データが混じる配列を処理し、失敗した項目は記録してスキップ
raw_items = ["100", "200", "bad", "300", None, "400"]
ok_items = []
skipped = []

for item in raw_items:
    try:
        ok_items.append(int(item))
    except (ValueError, TypeError) as e:
        print(f"[スキップ] {type(e).__name__}: {e} -> item={item!r}")
        skipped.append(item)

print("処理できた項目:", ok_items)
print("スキップした項目:", skipped)
実行結果
[スキップ] ValueError: invalid literal for int() with base 10: 'bad' -> item='bad'
[スキップ] TypeError: int() argument must be a string, a bytes-like object or a real number, not 'NoneType' -> item=None
処理できた項目: [100, 200, 300, 400]
スキップした項目: ['bad', None]

「エラーの個所だけ除外して前に進む」戦略は、データ処理の現場で非常に実用的です

まとめ

try-exceptは「エラーが発生しても止まらずに処理を継続する」ための基本技術です。

重要なのは、

  1. 想定される例外を具体的に捕まえる
  2. 原因を記録して握りつぶさない
  3. tryの範囲を最小にする
  4. デフォルト値・スキップ・リトライなどの継続戦略を使い分ける

この4点です。

これらを徹底することで、プログラムは現実世界の不確実性に強くなります。

まずは本記事のサンプルを手元で動かし、「落ちないコード」の感覚を身につけてください。

この記事を書いた人
エーテリア編集部
エーテリア編集部

人気のPythonを初めて学ぶ方向けに、文法の基本から小さな自動化まで、実際に手を動かして理解できる記事を書いています。

クラウドSSLサイトシールは安心の証です。

URLをコピーしました!