0Pricing
Python Academy · レッスン

__enter__ と __exit__ プロトコル

特殊メソッドのプロトコルを使ってコンテキストマネージャーを作成します。

「__enter__ と __exit__ プロトコル」はCoddyKit上の無料Python Academyレッスンです。 これはレッスン2/4です。 下記で完全なレッスンを無料で読むことができます。その後、ブラウザ内の組み込みコードエディタと24時間対応のAIチューターでハンズオン演習できます。 これはPython Academy学習パスの一部であり、ウェブとCoddyKitアプリ全体で進捗が同期されます。 Python Academyコースには全4レッスンが含まれています。

コンテキストマネージャープロトコル

__enter__と__exit__を定義するオブジェクトはすべてコンテキストマネージャーであり、withと一緒に使用できます。

class Managed:
    def __enter__(self):
        print("entering")
        return self

    def __exit__(self, exc_type, exc_val, exc_tb):
        print("exiting")
        return False

with Managed() as m:
    print("inside")

__enter__の返り値

__enter__はwithブロックの開始時に呼び出されます。その返り値が、asの後にある変数へ代入されます。

class Resource:
    def __enter__(self):
        self.value = 42
        return self  # bound to 'r'

    def __exit__(self, *args):
        self.value = None
        return False

with Resource() as r:
    print(r.value)  # 42

__exit__の引数

__exit__(exc_type, exc_val, exc_tb)は、エラーが発生した場合に例外情報を受け取ります。例外がない場合、3つの値はすべてNoneです。

class Safe:
    def __enter__(self):
        return self

    def __exit__(self, exc_type, exc_val, exc_tb):
        if exc_type is ValueError:
            print(f"Suppressed ValueError: {exc_val}")
            return True   # suppress the exception
        return False      # re-raise anything else

with Safe():
    raise ValueError("oops")  # suppressed

例外の抑制

__exit__からTrueを返すと、例外を抑制できます。False(または何も返さない場合)は、例外を伝播させます。

class Ignore:
    def __enter__(self): return self
    def __exit__(self, *args): return True  # swallow all exceptions

with Ignore():
    1 / 0

print("continues here")

データベース接続の例

典型的な使用例は、開始時に接続を取得し、終了時にコミットまたはロールバックして閉じることです。

class DBConn:
    def __enter__(self):
        self.conn = connect_db()
        return self.conn

    def __exit__(self, exc_type, exc_val, exc_tb):
        if exc_type:
            self.conn.rollback()
        else:
            self.conn.commit()
        self.conn.close()
        return False

タイマーのコンテキストマネージャー

__enter__で開始時刻を記録し、__exit__で差分を計算することで、経過時間を測定できます。

import time

class Timer:
    def __enter__(self):
        self.start = time.perf_counter()
        return self

    def __exit__(self, *args):
        self.elapsed = time.perf_counter() - self.start
        print(f"Elapsed: {self.elapsed:.3f}s")
        return False

with Timer():
    sum(range(1_000_000))

コンテキストマネージャーのネスト

1行のwithで複数のコンテキストマネージャーを組み合わせられます。

with open("in.txt") as src, open("out.txt", "w") as dst:
    dst.write(src.read())

再利用可能なマネージャーと1回限りのマネージャー

__enter__が毎回新しいリソースを確保するコンテキストマネージャーは再利用可能です。ロックを1回だけ保持する場合のように、内部状態を使い果たすものは再利用できない場合があります。

class Reusable:
    def __enter__(self):
        return []
    def __exit__(self, *a):
        return False

for _ in range(3):
    with Reusable() as lst:
        lst.append(1)
        print(lst)  # [1] each time

__aenter__と__aexit__(非同期)

非同期コンテキストマネージャーでは、__aenter__と__aexit__のコルーチンを定義し、async withを使用します。

class AsyncDB:
    async def __aenter__(self):
        self.conn = await connect()
        return self.conn

    async def __aexit__(self, *args):
        await self.conn.close()

# async with AsyncDB() as conn:
#     ...

ファイルオブジェクトのラップ

Pythonのファイルオブジェクトは、すでにコンテキストマネージャープロトコルを実装しています。そのため、f.close()を手動で呼び出さないでください。

with open("data.txt", "r") as f:
    data = f.read()
# file is closed here automatically
print(f.closed)  # True

Lockの実装

スレッドロックはコンテキストマネージャーです。__enter__がロックを取得し、__exit__が解放します。

import threading

lock = threading.Lock()

def update_shared():
    with lock:
        # critical section
        pass

クイックチェック

例外を抑制するには、__exit__は何を返す必要がありますか?

まとめ

__enter__と__exit__を実装して、コンテキストマネージャーを作成します。__enter__からリソースを返し、__exit__でクリーンアップと例外の抑制を処理します。

よくある質問

「__enter__ と __exit__ プロトコル」レッスンは無料ですか?

はい。「__enter__ と __exit__ プロトコル」の完全なテキストはこのウェブで無料で読めます。インタラクティブに演習し(組み込みコードエディタと24時間対応のAIチューター)、Python Academyコースの残りをアンロックするには、CoddyKit PROにアップグレードしてください。 Python Academyコースには全4レッスンが含まれています。

「__enter__ と __exit__ プロトコル」で何を学びますか?

特殊メソッドのプロトコルを使ってコンテキストマネージャーを作成します。 ブラウザで直接実行するハンズオンコードでPython Academyを演習し、24時間対応のAIチューターがレッスンを進める中での質問に答えます。

Python Academyを始めるのに経験は必要ですか?

事前経験は必要ありません。CoddyKitのPython Academyは初級者から上級者向けに構成されているため、ここから始めるか最初から始めて、自分のペースで進むことができます。 これはレッスン2/4です。

「__enter__ と __exit__ プロトコル」レッスンにはどのくらい時間がかかりますか?

ほとんどのCoddyKitレッスンは約5~10分かかります。各レッスンはコンパクトでインタラクティブなので、着実に進歩し、ウェブとアプリ全体で正確に前回の場所から再開できます。

このPython Academyレッスンでコードを書いて実行できますか?

はい。すべてのPython Academyレッスンに組み込みコードエディタが含まれているため、ブラウザでリアルコードを書いて実行し、即座のAIフィードバックを取得できます。ローカル設定は不要です。

このコースのすべてのレッスン

  1. with 文を深く理解する
  2. __enter__ と __exit__ プロトコル
  3. contextlib: @contextmanager
  4. 実践的なコンテキストマネージャーパターン
← Python Academyに戻る