Python contextlibとは?with文を自作する方法を解説

Pythonのcontextlibは、with文を自分で作るための道具をまとめたモジュールです。

with open(...)の「必ず後始末される」仕組みを、自分の処理にも持ち込めます。


with文が何をしているか

contextlibを理解するには、まずwithの仕組みを押さえる必要があります。

class Timer:
    def __enter__(self):
        print("  __enter__ が呼ばれた")
        return "戻り値"

    def __exit__(self, exc_type, exc, tb):
        print(f"  __exit__ が呼ばれた exc_type={exc_type}")
        return False

with Timer() as t:
    print("  中身:", t)

実行結果:

  __enter__ が呼ばれた
  中身: 戻り値
  __exit__ が呼ばれた exc_type=None

withは3つのことをします。

  1. __enter__()を呼ぶ
  2. その戻り値をasの変数に入れる
  3. ブロックを抜けるとき__exit__()を呼ぶ

例外が起きても__exit__は必ず呼ばれる

try:
    with Timer():
        raise ValueError("失敗")
except ValueError as e:
    print("  外で捕捉:", e)

実行結果:

  __enter__ が呼ばれた
  __exit__ が呼ばれた exc_type=<class 'ValueError'>
  外で捕捉: 失敗

これがwithの価値です。ファイルのクローズ、ロックの解放、接続の切断——「必ずやらなければならない後始末」を書き忘れずに済みます。

__exit__には発生した例外の情報が渡ってきます。何も起きなければNoneです。


__exit__がTrueを返すと例外が消える

class Swallow:
    def __enter__(self): return self
    def __exit__(self, *a): return True

with Swallow():
    raise ValueError("消える")

print("  例外が握りつぶされた")

実行結果:

  例外が握りつぶされた

Trueを返すと「この例外は処理した」という意味になり、外へ伝わりません。False(またはNone)なら、そのまま外へ投げられます。

意図せずTrueを返すとエラーが消えます。握りつぶすつもりがないなら、__exit__では何も返さないでください。


contextlibで書くと短くなる

クラスを書かずに、関数だけで同じことができます。

from contextlib import contextmanager

@contextmanager
def tag(name):
    print(f"<{name}>")
    yield
    print(f"</{name}>")

with tag("p"):
    print("  本文")

実行結果:

<p>
  本文
</p>

yieldの前が__enter__、後ろが__exit__に相当します。クラス定義が不要になり、ぐっと読みやすくなります。

詳しくは「Python @contextmanagerとは?with文を関数で作る方法」で解説しています。


contextlibの主な機能

機能 用途 解説記事
@contextmanager with文を関数で作る contextmanager
suppress() 特定の例外を無視する suppress
closing() close()だけの相手をwithで扱う closing
redirect_stdout() 出力先を差し替える redirect_stdout
ExitStack() 数が決まらない後始末をまとめる ExitStack
nullcontext() 「何もしない」with 下記参照

nullcontext|条件によってwithを使い分ける

from contextlib import nullcontext

def process(path=None):
    cm = open(path, encoding="utf-8") if path else nullcontext()
    with cm as f:
        return f.read() if f else "(標準入力の代わり)"

print(process("a.txt"))
print(process())

実行結果:

A
(標準入力の代わり)

「ファイルが指定されたら開く、なければ何もしない」——同じwithで両方を書けます。ifで処理を二重に書かずに済みます。

nullcontext()はNoneを返すだけの、何もしないコンテキストマネージャです。


いつ自作するか

場面 例
必ず後始末が必要 接続の切断・ロックの解放・一時ファイルの削除
前後で対になる処理 計測の開始と終了・インデントの増減
状態を一時的に変える 設定の差し替え・作業ディレクトリの移動
使い終わりを強制したい 使う側にclose()を忘れさせない

逆に、後始末が不要ならwithにする意味はありません。単なる関数で十分です。


contextlibのまとめ

  • withは__enter__と__exit__を呼ぶ仕組み
  • 例外が起きても__exit__は必ず実行される
  • __exit__がTrueを返すと例外が消える
  • @contextmanagerならクラスを書かずに済む
  • 例外を無視するならsuppress()、close()だけの相手にはclosing()
  • 数が決まらないならExitStack()

あわせて読みたい

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

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