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

コンテキストマネージャ

Python には with 文という文法があり、これを使うと前処理と後処理を記述することができます。
その際、with 文にはコンテキストマネージャという種類のオブジェクトを渡すことが取り決めになっています。

コンテキストマネージャは、主にリソースを適切に管理するために使用されます。
コンテキストマネージャの最も一般的な使い方は、ファイルの操作で利用されます。:

with open( 'workfile'、 'r')as f:
    pass

しかし、それが実際にどのように実際に動作するのか、どのようにして独自のものを作り出すのかを紹介します。

実際、コンテキストマネージャは、__enter__と __exit__のメソッドを実装する単なるクラスを作成するだけで良いです。

with文でコンテキストマネージャが呼び出されると、withブロックの直前に、
必ず__enter__が呼び出され、
withブロックの処理が正常終了(または例外発生)したならば、
必ずコンテキストマネージャの __exit__ が呼び出される仕組みです。

#コンテキストマネージャの定義
class MyManager:

    def __enter__(self):
        # 最初に実行される (前処理)
        print('--- start ---')

    def __exit__(self, exc_type, exc_value, traceback):
        # 最後に実行される (後処理)
        print('---- end ----')

with MyManager():
    print( 'hello')
--- start ---
hello
---- end ----

contextlib.contextmanager

次は contextlib.contextmanager を使用してコンテキストマネージャを実装してみます。
ジェネレータ関数を @contextlib.contextmanager デコレータで修飾すると、それがコンテキストマネージャになります。
yield の箇所が with 文のブロック内の処理に置き換わって、
yield文前後が __enter__ と __exit__ に相当するようなイメージと考えると理解しやすと思います。

import contextlib

@contextlib.contextmanager
def myfunction():
    print('---start---')
    yield
    print('---end---')

with myfunction():
    print('hello')
---start---
hello
---end---

例外への対処

上記ソースでは例外発生時に対処できないので、以下のようにするのが望ましいです。

import contextlib

@contextlib.contextmanager
def myfunction():
    try:
        #前処理
        print('---start---')
        yield
    except Exception:
        #例外処理
        pass
    finally:
        #後処理
        print('---end---')

with myfunction():
    print('hello')
---start---
hello
---end---