メインコンテンツにスキップ
モジュール 9 · レッスン 3 / 4コース内の 35/36~12 min
モジュールのレッスン (3/4)

コンテキストマネージャー: および __enter__/__exit__ を使用

コンテキスト マネージャー は、with で使用するオブジェクトです。それはそれを保証します 特定のセットアップ/クリーンアップ操作は、エラーが発生した場合でも常に実行されます。 標準的な例: ファイルを開いて閉じる。

with パターン

Python
with open("note.txt", "w") as f:
    f.write("ciao")
# qui f è già chiuso, anche se write() ha sollevato un'eccezione

平易な英語: 「with resource X open as f, do this. 完了したら (何が起こっても) X を閉じてください。」 with がなければ、次のように書く必要があります。 毎回 try/finally が返されます。

一度に複数

Python
with open("a.txt") as f, open("b.txt") as g:
    dati_a = f.read()
    dati_b = g.read()

独自のコンテキスト マネージャー (クラス) を作成する

__enter__ と __exit__ という 2 つのダンダー メソッドを定義する必要があります。

Python
class Timer:
    def __enter__(self):
        import time
        self.t0 = time.perf_counter()
        return self  # è l'oggetto che finisce dopo "as"
    def __exit__(self, exc_type, exc_value, traceback):
        import time
        self.elapsed = time.perf_counter() - self.t0
        # ritornare True ingoia l'eccezione; di solito non lo vuoi
        return False

with Timer() as t:
    sum(range(10**6))
t.elapsed  # quanto è durato
  • ブロックに入ると __enter__(self) が呼び出されます。戻り値は、as の後に得られる値です。
  • __exit__(self, exc_type, exc_value, tb) は終了時に (常に) 呼び出されます。 True が返された場合、例外は抑制されます。

ショートカット: @contextmanager

contextlib.contextmanager を使用すると、コンテキスト マネージャーを ジェネレータ関数:

Python
from contextlib import contextmanager

@contextmanager
def timer():
    import time
    t0 = time.perf_counter()
    try:
        yield  # qui sta il blocco "with"
    finally:
        elapsed = time.perf_counter() - t0
        print(f"durata: {elapsed:.4f}s")

with timer():
    sum(range(10**6))

コード 前 yield は __enter__、コード 後 は __exit__ です。 try/finally は、エラーが発生した場合でもクリーンアップを保証します。

抑制: 例外を無視します

Python
from contextlib import suppress

with suppress(FileNotFoundError):
    open("forse-non-esiste.txt").read()
# se il file non c'è, nessun errore propaga

落とし穴: with をサポートするオブジェクトのみ

Python
x = 42
with x:  # AttributeError: __enter__
    ...

@contextmanager を使用してコンテキスト マネージャーを作成する

標準ライブラリの contextlib モジュールは、__enter__ メソッドと __exit__ メソッドを実装する冗長なクラスを作成する代わりに、ジェネレーター関数を使用してコンテキスト マネージャーを作成するために、@contextmanager という非常に便利なデコレーターをエクスポートします。単一の yield を使用するジェネレーター関数を作成するだけです。

Python
from contextlib import contextmanager

@contextmanager
def manage_resource():
    # __enter__ code
    yield resource
    # __exit__ code

自分で試してみてください

運動#python.m9.l3.e1
試行回数: 0読み込み中…

contextlib.suppress を使用して、ValueError を無視して int('non-numero') を試行します。ブロックの後に「ok = True」を代入します。 「OK」と評価します。

エディターを読み込み中…
ヒントを表示

suppress(ValueError) は ValueError のみを飲み込みます。

3 回の試行後に解決策が利用可能になります

復習演習

運動#python.m9.l3.e2
試行回数: 0読み込み中…

@contextmanager を使用して、空のリストを生成し、終了時にそれをタプルに変換する (出力する) コンテキスト マネージャー `collect` を作成します。これを使用して、ブロック内のリストに 1、2、3 を追加します。最後に、`len(collection)` を評価します。ここで、`collection` は生成されたリストです。

エディターを読み込み中…
ヒントを表示

アイテムを生成して、with ブロック内のリストを公開します。

3 回の試行後に解決策が利用可能になります

追加の課題

運動#python.m9.l3.e3
試行回数: 0読み込み中…

Write a custom context manager using the `@contextmanager` decorator from `contextlib`. The context manager must be named `mock_session`. Upon entry, it must append `'start'` to a global list `log_list = []`, yield control using `yield`, and upon exit append `'stop'`. Finally, evaluate `log_list` after using the context manager with `with mock_session():`.

エディターを読み込み中…
ヒントを表示

「stop」を追加する前に実行を一時停止するには、mock_session 内で yield を使用します。

3 回の試行後に解決策が利用可能になります