Pythonのwithを理解する

投稿日: 更新日:

端的にwith文とは

ある処理のに必要な処理を扱う物です。

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

コンテキストマネージャーとはwith文の実行に必要な物を定義したオブジェクトです。

コードは以下の通りです

class ContextManager:
    def __init__(self):
        print("初期化")

    def __enter__(self):
        print('前処理')

    def __exit__(self, exc_type, exc_value, traceback):
        """
        exc_type: 例外のクラス
        exc_value: メッセージ
        traceback: tracebackオブジェクト
        """
        print("後処理")

これをwith文で実行すると以下のようになります。

with ContextManager():
    print("何らかの処理")

実行結果

初期化
前処理
何らかの処理
後処理

処理の流れを画像でまとめると以下の通りです

with文の流れ

※補足:with文は__enter__が正常に終了すれば__exit__は必ず実行されます。

引数、戻り値

  • __init__で引数を受け取る
  • __enter__の戻り値はasで受け取る

例としてファイルのオープンを考えます。

コンテキストマネージャーを以下のように定義します。

class ContextManager:
    def __init__(self, path):
        print("初期化")
        self.path = path
        self.file = None

    def __enter__(self):
        print('前処理')
        self.file = open(self.path)
        return self.file

    def __exit__(self, exc_type, exc_value, traceback):
        print("後処理")
        print(f'exit: {exc_type}, {exc_value}, {traceback}')
        self.file.close()

これを用いてコードを書くと以下のようになります。

with ContextManager("text.txt") as f:
    print(f.readline())

例外

with文中で例外が起こった場合すべて__exit__に与えられます。

with文での例外の流れ

with文が例外で終了された場合、 __exit__の戻り値がTrueであれば例外は抑制されwithの続きが処理されます。Falseであれば例外が再送出されます。

例えばこのようなクラスを定義します

class ContextManager:
    def __init__(self, retu):
        self.retu = retu

    def __enter__(self):
        pass

    def __exit__(self, exc_type, exc_value, traceback):
        print(f'exit: {exc_type}, {exc_value}, {traceback}')
        return self.retu

戻り値をTrueにして実行すると

with ContextManager(True):
    raise ValueError("Error!")
print("next...")

出力はこのようになります

exit: <class 'ValueError'>, Error!, <traceback object at 0x000002084EA37340>
next...

このように例外が発生しても次の処理が行われていることが分かります。

書いた人

profile_image

お茶の葉

物理とプログラミングが好きな人