with/ContextManagerのメモ書き【Python】

目次

コピペ&結論メモ

PYTHONCOPY
class testContextManager:
#②enterが実行されて、値を返却(例だとself)
def __enter__(self):
print("enter")
return self

#⑥ブロック実行後にexitが実行される
def __exit__(self, ex_type, ex_value, trace):
print("exit")

#⑤enterの返却値(self)を利用してメソッドを実行
def method(self):
print("method")
 
#①with文で指定したコンテキストマネージャークラスのenterが実行
#③enterの返却値がtargetにセットされる
with testContextManager() as target:
#④ブロック内が実行される
    print("block")
target.method()

実行結果(>の先が出力結果):
>enter
>block
>method
>exit

自分的まとめ:
前後処理を共通化したい時に使う。
コンテキストマネージャが生成されるときにenterが呼ばれ、Withブロックから抜ける時にexitが呼ばれる。例外が発生しても必ずexitが実行されるので、finalとして使うことで、後処理書き忘れ防止になる。

__enter__と__exit__を書くのが面倒・不要な場合は、デコレータを使ったコンテキストマネージャーを用意することで対応。

@contextlib.contextmanagerについて

ジェネレーターをこの@contextlib.contextmanagerでデコレートするとコンテキストマネージャーになる。

ジェネレーターとは、returnの代わりにyieldで値を返す関数のこと。

この場合、__enter__と__exit__の実装が不要になる。

PYTHONCOPY
from contextlib import contextmanager

#②コンテキストマネージャーになったfuncが実行される。
@contextmanager
def func():
    try:
        print("func")
③yieldで指定した値を返却する。
        yield "yield"
    finally:
⑤withのブロックを抜けた後、finallyが実行される。
        print("final")

#①with文で指定したコンテキストマネージャーを実行
with func() as target:
    #④yieldの値がtargetセットされ、ブロック内が実行される
    print("block")
    print(target)

実行結果(>の先が出力結果):
>func
>block
>yield
>final

その他の気づいたこと、学んだことメモ

  • exitで、Trueを返却すればエラーを握りつぶせる
  • withをつかわず、単純にインスタンスを生成して実行した場合、コンテキストマネージャが動かないので、enterとexitは勝手に動くことはない。
  • ファイル操作はそもそもコンテキストマネージャー型だから、自分でコンテキストマネージャーを作る必要が無く、勝手にクローズされる。
  • デコレートしたコンテキストマネージャからすればどんなブロックが実行されるか分からないので、yieldをtry/finalで囲む必要がある。囲まないと、後処理がエラーだと動かなくなる事態になる。
Weekly Most Popular
記事書いたりサイト運営してる人
シイタ / siita
シイタ / siita
現役サラリーマン/システムエンジニアです。 会社外でも、自分に自信を持って活躍できるようになりたい。金銭的にも安心できるようになりたい。という気持ちから技術向上や不労所得を得るために、日々活動することを記録し、共有していきます。