Pythonではwith文で前処理、後処理を行えるようにする場合、そのオブジェクトはコンテキストマネージャである必要があります。
特殊メソッドを実装する
次のように特殊メソッド__enter__、__exit__を実装すれば、それはすでにコンテキストマネージャです。__enter__の処理、withブロックの処理、__exit__の処理が順に実行されます。
class ContextManagerTest:
def __enter__(self):
print('__enter__')
def __exit__(self, exc_type, exc_value, traceback):
print('__exit__')
with ContextManagerTest():
print('with')
__enter__ with __exit__
__exit__の引数で渡される情報は例外発生時に活用できます。ここでは__exit__の戻り値としてTrueを返していますが、この場合は例外が発生しても処理がストップすることはありません。そのまま処理を継続します。Falseを返した場合は無視できないエラーとして、通常の例外のように処理がそこでストップします。
class ContextManagerTest:
def __enter__(self):
print('__enter__')
def __exit__(self, exc_type, exc_value, traceback):
print('__exit__')
print(exc_type)
print(exc_value)
print(traceback)
return True
with ContextManagerTest():
val = int('abc')
__enter__ __exit__ <type 'exceptions.ValueError'> invalid literal for int() with base 10: 'abc' <traceback object at 0x0000000002A7E6C8>
asで渡されるオブジェクトは「__enter__」の戻り値で指定することができます。
class ContextManagerTest:
def __enter__(self):
print('__enter__')
return 'as obj'
def __exit__(self, exc_type, exc_value, traceback):
print('__exit__')
with ContextManagerTest() as as_obj:
print(as_obj)
__enter__ as obj __exit__
デコレータを使用する
こちらはデコレータを使用するやり方です。より簡単にコンテキストマネージャを作成することができ、クラスだけではなく関数もコンテキストマネージャとすることができます。
from contextlib import contextmanager
@contextmanager
def context_manager_test():
print('enter')
yield
print('exit')
with context_manager_test():
print('with')
enter with exit
asで渡されるオブジェクトは「yield」で指定することができます。
from contextlib import contextmanager
@contextmanager
def context_manager_test():
print('enter')
yield 'as obj'
print('exit')
with context_manager_test() as as_obj:
print(as_obj)
enter as obj exit
