【Python】コンテキストマネージャーの基本 ~ with…as命令にオブジェクトを渡すには ~

Python のコンテキストマネージャーの基本について解説します。
目次
コンテキストマネージャの基本
Python では、ファイル操作などのリソースのクリーンアップ処理のためによく with ... as ... を使用します。with に渡すオブジェクトはコンテキストマネージャーに対応している必要があります
この記事では、コンテキストマネージャーの基本について紹介します。
with ... as ...
コンテキストマネージャーの話の前に、with ... as ... について説明します。例として、Python でファイルの入出力を考えてみます。
ファイルを操作する場合には、処理が終わったらクローズ処理が必要です。クローズ処理は忘れてしまうことが多いのですが、以下のように with ... as ... を使用することでクローズ忘れを防止できます。
with open("sample.txt", "r", encoding="UTF-8") as file:
data = file.read()
print(data)【実行結果】 サンプルファイル
例のように with ... as ... を使用すると読み込みの read の後にファイルをクローズします。これにより、ファイルのクローズ忘れを防止できます。Python では、try ... finally ... でも記載できますが、シンプルかつ明瞭なコードとして表現できるため、with ... as ... が利用されます。
with ... as ... にオブジェクトを渡せるかどうかはコンテキストマネージャーに対応しているかどうかで決まります。上記で紹介したファイル入出力で with ... as ... が使用できるのはコンテキストマネージャーに対応しているためです。
なお、コンテキストマネージャーは自分で実装することが可能です。以降では、コンテキストマネージャーを自分で実装する際の基本を説明していきます。
コンテキストマネージャーの実装
コンテキストマネージャーであるためには、対象のクラスが「__enter__」と「__exit__」というメソッドを実装している必要があります。
| 実装するメソッド | 概要 |
|---|---|
__enter__(self) | with に入った時に呼び出されるメソッド。リソースを準備し、戻り値を as で指定された変数に反映する。 |
__exit__(self, exception_type, exception_value, traceback) | with 句を抜けるときに呼び出されるメソッド。exception_type、exception_value、traceback は with 内で例外発生した場合に、例外の型、値、トレースバックが渡される。 |
__enter__ メソッドは、with に入った時に呼び出されるメソッドです。return の戻り値が as で指定した変数に反映されます。
__exit__ メソッドは、with を抜けるときに呼び出されるメソッドです。このメソッドで不要になったリソースに対する処理などを実装します。引数の「exception_type」「exception_value」「traceback」は、with 内で例外が発生した場合に、発生した例外の型や値、トレースバック情報が渡されます。
コンテキストマネージャーの実装例
例として、コンテキストマネージャーの条件を満たした SampleContext というクラスを実装した例を紹介します。
class SampleContext:
# コンテキストを作成する
def __enter__(self):
print("--- コンテキスト開始(enter) ---")
return self
# コンテキストを終了する
def __exit__(self, exception_type, exception_value, traceback):
print("--- コンテキスト終了(exit) ---")
if exception_type is None:
print("エラーなし")
else:
print(f"エラー: {exception_type}, {exception_value}, {traceback}")
return True
# 任意の関数
@staticmethod
def test():
print("testメソッド")
def main():
with SampleContext() as sample:
sample.test()
# # 何かしらエラーが発生すると挙動が変わる
# raise ValueError("不正な値")
print("後続処理")
if __name__ == "__main__":
main()【実行結果】 --- コンテキスト開始(enter) --- testメソッド --- コンテキスト終了(exit) --- エラーなし 後続処理
例では、SampleContext という独自クラスを作成し、main() の中の with 内で呼び出しを行っています。
with に入った際に「--- コンテキスト開始(enter) ---」という文字列が表示されていることから __enter__ メソッドが呼び出されていることが分かります。
その後に sample.test() という SampleContext 内で定義したメソッドが呼び出せていることから、as で指定した sample には、return で指定した SampleContext クラスのオブジェクトが設定されています。
最後に処理を抜ける際には「--- コンテキスト終了(exit) ---」という文字列が表示されていることから __exit__ メソッドが呼び出されています。この処理は「後続処理」と print する前に表示されていることから with を抜けるときに実行されていることが分かります。
今回は、with 内では例外は発生していないため、exception_type には何も設定されておらず「エラーなし」と表示されます。
with で例外が発生した場合の挙動
with 内で例外が発生した場合について、以下で確認しましょう。
def main():
with SampleContext() as sample:
sample.test()
# 何かしらエラーが発生すると挙動が変わる
raise ValueError("不正な値")
print("後続処理")【実行結果例】 --- コンテキスト開始(enter) --- testメソッド --- コンテキスト終了(exit) --- エラー: <class 'ValueError'>, 不正な値, <traceback object at 0x000001BCFFC92440> 後続処理
例では、明示的に raise で ValueError を発生させています。その結果 __exit__ メソッドで例外情報が入力されて挙動が変わっています。
__exit__ メソッドは返却値として True を返却しています。これは、例外をコンテキストマネージャー内で処理済みであることを意味しています。返却値を return False とするか、もしくは返却値を指定しない場合には、上位の例外ハンドラが呼び出されて最後にトレースバックが表示されます。
# コンテキストを終了する
def __exit__(self, exception_type, exception_value, traceback):
print("--- コンテキスト終了(exit) ---")
if exception_type is None:
print("エラーなし")
else:
print(f"エラー: {exception_type}, {exception_value}, {traceback}")
return False【実行結果】
--- コンテキスト開始(enter) ---
testメソッド
--- コンテキスト終了(exit) ---
エラー: <class 'ValueError'>, 不正な値, <traceback object at 0x0000026568C82440>
Traceback (most recent call last):
...省略...
raise ValueError('不正な値')
ValueError: 不正な値上記で見てきたようにコンテキストマネージャーの条件を満たすように __enter__ と __exit__ メソッドをクラスに記載することで with ... as ... で使用できるクラスを定義することができます。
まとめ
Python のコンテキストマネージャーの基本について解説しました。
コンテキストマネージャーであるには、実装クラスに「__enter__」と「__exit__」というメソッドが実装されている必要があります。この記事では、独自にコンテキストマネージャーに対応したクラスを作成する方法を紹介しました。
with ... as ... で使用可能なクラスを定義する際の参考にしてください。
上記で紹介しているソースコードについては GitHub にて公開しています。参考にしていただければと思います。



の基本.jpg)
の使い方.jpg)

