Python contextlib.suppress()とは?例外を無視する方法

Pythonのcontextlib.suppress()は、指定した例外を無視するwith文です。

try-except-passを2行に縮められます。「エラーが出ても構わない」処理を素直に書けます。


contextlib.suppress()の基本的な使い方

from contextlib import suppress
import os

with suppress(FileNotFoundError):
    os.remove("no_such_file.txt")

print("エラーにならず通過")

実行結果:

エラーにならず通過

ファイルが無くても落ちません。「消えていればそれでいい」という処理にぴったりです。


try-exceptとの比較

try:
    os.remove("no_such_file.txt")
except FileNotFoundError:
    pass
with suppress(FileNotFoundError):
    os.remove("no_such_file.txt")

結果は同じで、4行が2行になりました。

行数以上に大きいのが読みやすさです。except ...: passは「握りつぶしている」ことが一瞬分かりにくく、レビューで「ここ本当にpassでいいの?」と止まりがちです。suppressなら無視するのが意図だと一目で伝わります。


複数の例外を指定できる

from contextlib import suppress
import os

with suppress(FileNotFoundError, PermissionError):
    os.remove("no_such_file.txt")

カンマで並べるだけです。except (A, B):と同じ形になります。


指定していない例外は素通りする

from contextlib import suppress

with suppress(FileNotFoundError):
    1 / 0

実行結果:

ZeroDivisionError: division by zero

すべてを飲み込むわけではありません。指定した例外だけが消え、それ以外はそのまま外へ出ます。

これは安全側の設計です。想定外のエラーまで隠れてしまうと、原因の特定ができなくなります。


例外が起きた時点で残りはスキップされる

ここを誤解すると危険です。

from contextlib import suppress

with suppress(ValueError):
    print("  A")
    raise ValueError()
    print("  B")

print("  → Bは実行されない")

実行結果:

  A
  → Bは実行されない

「エラーを無視して次の行へ進む」のではありません。ブロックの残りを飛ばして、withの外へ出ます。

そのため、複数の処理をまとめて囲むと途中で止まっていることに気づけません。

with suppress(FileNotFoundError):
    os.remove("a.txt")
    os.remove("b.txt")
    os.remove("c.txt")

a.txtが無ければ、b.txtとc.txtは削除されません。それぞれ個別に囲むか、ループにしてください。

for path in ["a.txt", "b.txt", "c.txt"]:
    with suppress(FileNotFoundError):
        os.remove(path)

使ってよい場面・避けるべき場面

使ってよい 避けるべき
あれば消す(無くてもよい) 失敗を知る必要がある処理
あれば読む(無ければ既定値) データの保存・送信
後始末(切断・ロック解放) Exceptionを丸ごと指定する
統計情報の取得(失敗しても本筋に影響なし) 複数処理をまとめて囲む

特に避けたいのがsuppress(Exception)です。

with suppress(Exception):
    important_process()

これはあらゆるエラーを黙って消します。タイプミスによるNameErrorも、接続失敗も、すべて無言で通過します。except:と同じ問題です。

捕まえる例外は具体的に指定してください。


実践例|あれば消す

from contextlib import suppress
from pathlib import Path
import os

def cleanup(paths):
    for p in paths:
        with suppress(FileNotFoundError):
            os.remove(p)

cleanup(["tmp1.txt", "tmp2.txt", "no_such.txt"])
print("後始末完了")

「存在するか確認してから消す」よりも安全です。

if os.path.exists(p):
    os.remove(p)          # 確認と削除の間に消える可能性がある

確認した直後に別のプロセスが消すと、os.remove()が失敗します。やってみて、失敗したら受け止めるほうが確実です。

なおpathlibなら専用の引数があります。

Path("tmp.txt").unlink(missing_ok=True)

用意されているならそちらを使ってください。詳しくは「Python Path.unlink()とは?ファイルを削除する方法」で解説しています。


実践例|設定が無ければ既定値

from contextlib import suppress
import json

config = {"port": 8080}

with suppress(KeyError, TypeError):
    config["port"] = int(load_user_config()["port"])

print(config)

ユーザー設定にキーが無かったり型が違ったりしても、既定値のまま進みます。

ただしなぜ既定値になったかは記録に残りません。原因を知りたいなら、素直にtry-exceptでログを出してください。

try:
    config["port"] = int(load_user_config()["port"])
except (KeyError, TypeError):
    logging.warning("portの設定が読めません。既定値を使います")

suppressは「本当に何もしなくてよい」ときだけ——これが使い分けの基準です。


contextlib.suppress()のまとめ

  • suppress(例外)は指定した例外を無視する
  • try-except-passより短く、意図が伝わる
  • 指定していない例外はそのまま外へ出る
  • 例外が起きた時点で残りの行はスキップされる
  • 複数処理をまとめて囲まない——個別かループで
  • suppress(Exception)は使わない
  • 理由を記録したいならtry-except+ログ

あわせて読みたい

プログラミング学習に興味がある方は、まずは無料体験会へ

無料体験会・お申し込みはこちら