【Python】コンテキストマネージャの実装方法

Python

1. __enter__, __exit__を実装する

class ContextManagerTest:
    # コンテキストマネージャの入り口で実行される処理
    # asで受け取る値を返すことができる
    def __enter__(self):
        print("__enter__")
        return self
    
    # コンテキストマネージャの出口で実行される処理
    # with文内での例外はexc_type, exc_value, tracebackで情報を受け取ることができる。
    def __exit__(self, exc_type, exc_value, traceback):
        print("__exit__")
        print("exc_type: ", exc_type)
        print("exc_value: ", exc_value)
        print("traceback: ", traceback)

        print("==== close ====")

    def error(self):
        raise Exception("test exception")

with ContextManagerTest() as t:
    t.error()
__enter__
__exit__
exc_type:  <class 'Exception'>
exc_value:  test exception
traceback:  <traceback object at 0x00000279D4256200>
==== close ====
---------------------------------------------------------------------------
Exception                                 Traceback (most recent call last)
...省略

asで受け取った値をwith文外で利用することも可能。ただしその場合は__exit__で例外情報は受け取れない。Close処理などは問題なく実行される。

with ContextManagerTest() as t:
    # t.error()
    pass

t.error()
__enter__
__exit__
exc_type:  None
exc_value:  None
traceback:  None
==== close ====
---------------------------------------------------------------------------
Exception                                 Traceback (most recent call last)
...省略

例外の伝播を防ぎたい場合は__exit__でTrueを返すようにする。

class ContextManagerTest:
    # コンテキストマネージャの入り口で実行される処理
    # asで受け取る値を返すことができる
    def __enter__(self):
        print("__enter__")
        return self
    
    # コンテキストマネージャの出口で実行される処理
    # with文内での例外はexc_type, exc_value, tracebackで情報を受け取ることができる。
    def __exit__(self, exc_type, exc_value, traceback):
        print("__exit__")
        print("exc_type: ", exc_type)
        print("exc_value: ", exc_value)
        print("traceback: ", traceback)

        print("==== close ====")

        return True

    def error(self):
        raise Exception("test exception")


with ContextManagerTest() as t:
    t.error()
__enter__
__exit__
exc_type:  <class 'Exception'>
exc_value:  test exception
traceback:  <traceback object at 0x00000279D82D7240>
==== close ====

2. デコレータを利用する

contextlibモジュールのcontextmanagerデコレータを利用することで、コンテキストマネージャのファクトリ関数を定義することができる。

from contextlib import contextmanager

@contextmanager
def contextmanager_func(*args, **kwds):
    print("__enter__")

    # 必要なリソースの生成
    t = Test()
    t.open()
    try:
        yield t
    finally:
        # 生成したリソースの開放
        t.close()

# 動作確認用クラス
class Test:
    def error(self):
        raise Exception("test exception")
    
    def open(self):
        print("==== open ====")
    
    def close(self):
        print("==== close ====")

# 通常のコンテキストマネージャと同様にwith文で利用可能
with contextmanager_func() as t:
    print(type(t))
    t.error()
__enter__
==== open ====
<class '__main__.Test'>
==== close ====
---------------------------------------------------------------------------
Exception                                 Traceback (most recent call last)
c:\github\TIL\machine_learning\hands_on_machine_learning_with_scikit-learn, keras, and tensorflow\chapter2.ipynb Cell 42 in 2
     25 with contextmanager_func() as t:
     26     print(type(t))
---> 27     t.error()

c:\github\TIL\machine_learning\hands_on_machine_learning_with_scikit-learn, keras, and tensorflow\chapter2.ipynb Cell 42 in 5
      4 def error(self):
----> 5     raise Exception("test exception")

Exception: test exception

例外を処理はtry文の箇所で可能。ただし__enter__, __exit__の時と同様に、with文の例外は処理できない。

@contextmanager
def contextmanager_func(*args, **kwds):
    print("__enter__")

    t = Test()
    t.open()
    try:
        yield t
    except Exception as e:
        print(e)
    finally:
        t.close()


with contextmanager_func() as t:
    pass

t.error()
__enter__
==== open ====
==== close ====
---------------------------------------------------------------------------
Exception                                 Traceback (most recent call last)
c:\github\TIL\machine_learning\hands_on_machine_learning_with_scikit-learn, keras, and tensorflow\chapter2.ipynb Cell 42 in 3
     27 with contextmanager_func() as t:
     28     pass
---> 30 t.error()

c:\github\TIL\machine_learning\hands_on_machine_learning_with_scikit-learn, keras, and tensorflow\chapter2.ipynb Cell 42 in 5
      4 def error(self):
----> 5     raise Exception("test exception")

Exception: test exception

コメント

タイトルとURLをコピーしました