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

PythonBeginner
オンラインで実践に進む

はじめに

Python のコンテキストマネージャは広く利用されていますが、その使用目的を理解している人は多くありません。ファイル読み書きなどで一般的に使用されるこれらのステートメントは、特定のプロセスでのみ特定のリソースが使用されることを保証することで、アプリケーションがシステムメモリを節約し、リソース管理を改善するのに役立ちます。

この実験(Lab)では、自動リソース管理のために with ステートメントの使用方法を学び、その後、2 つの異なる方法(関数ベースのアプローチとクラスベースのアプローチ)を使用して独自のコンテキストマネージャを作成します。

with ステートメント

このステップでは、ファイルオブジェクトのようなコンテキストマネージャと共に with ステートメントを使用する方法を学習します。コンテキストマネージャとは、コードブロック(コンテキスト)が開始および終了したときに通知を受け取るオブジェクトです。with ステートメントは、この通知プロセスを自動的に処理します。

例えば、ファイルオブジェクトはコンテキストマネージャです。with ブロックが終了すると、ブロック内でエラーが発生した場合でも、ファイルオブジェクトは自動的に閉じられます。これにより、データ損失やリソースリークを防ぎます。

注記: WebIDE(VS Code のようなもの)の使用に慣れていない場合は、まずこちらの実験 (lab)で学習してください。

実際に見てみましょう。左側のファイルエクスプローラで main.py ファイルを開きます。以下のコードをコピーして貼り付けてください。このコードは、すべて with ステートメントを使用して、まずファイルを作成して書き込みを行い、次にその内容を読み取ります。

## 'with' ステートメントを実証するためにファイルを作成します。
## まず、ファイルにいくつかの内容を書き込みます。
with open('my_file.txt', 'w') as f:
    f.write('This is a test file.')

## 次に、コンテキストマネージャを使用して読み込みます。
## 'with' ブロックを抜けると、ファイルは自動的に閉じられます。
with open('my_file.txt') as f:
    file_contents = f.read()
    print(file_contents)

## ここでファイルは、エラーが発生した場合でも自動的に閉じられます。
print("\nFile has been read and closed.")

次に、ターミナルからスクリプトを実行して出力を確認します。

python3 main.py

ファイルが正常に書き込まれ、読み込まれたことを確認する以下の出力が表示されるはずです。with ステートメントにより、書き込み操作と読み取り操作の両方の後で my_file.txt が適切に閉じられることが保証されます。

This is a test file.

File has been read and closed.

with ステートメントを使用することは、ファイルやネットワーク接続などのリソースを扱うための推奨される方法です。なぜなら、コードがよりクリーンで安全になるからです。

独自のコンテキストマネージャの記述

このステップでは、ジェネレータ関数と @contextlib.contextmanager デコレータを使用してカスタムコンテキストマネージャを作成する方法を学びます。これは、単純なコンテキストマネージャを作成するための簡潔な方法です。

yield ステートメントの前のコードは with ブロックに入るときに実行され(セットアップロジック)、yield ステートメントの後のコードはブロックを終了するときに実行されます(クリーンアップロジック)。yield された値は、with ステートメントで as キーワードを使用して変数に割り当てることができます。

ファイルエクスプローラから function_cm.py ファイルを開いてください。以下のコードをファイルにコピーします。

## contextlib デコレータを使用した関数ベースのコンテキストマネージャ
import contextlib

@contextlib.contextmanager
def context_manager(num):
    print('Enter')  ## yield 前のコードは __enter__ で実行される
    yield num + 1   ## yield された値が 'cm' 変数になる
    print('Exit')    ## yield 後のコードは __exit__ で実行される

with context_manager(2) as cm:  ## cm は yield された値 (3) を受け取る
    print('Right in the middle with cm = {}'.format(cm))

次に、ターミナルでスクリプトを実行します。

python3 function_cm.py

以下の出力が表示されます。実行順序に注意してください:yield 前のコードが最初に実行され、次に with ブロック内のコードが実行され、最後に yield 後のコードが実行されます。

Enter
Right in the middle with cm = 3
Exit

このアプローチは、データベース接続のセットアップと切断、または設定の一時的な変更など、一時的なコンテキストを作成するのに非常に役立ちます。

クラスベースのコンテキストマネージャ

このステップでは、クラスとしてコンテキストマネージャを実装する方法を学びます。このアプローチはより多くの制御を提供し、より複雑なシナリオに適しています。クラスベースのコンテキストマネージャを作成するには、__enter__ と __exit__ という 2 つの特殊メソッドを定義する必要があります。

  • __enter__: このメソッドは with ステートメントに入るときに呼び出されます。セットアップロジックを実行する必要があります。返り値は、指定されている場合、as の後の変数に割り当てられます。
  • __exit__: このメソッドは、正常終了または例外により with ブロックを終了するときに呼び出されます。クリーンアップロジックを実行する必要があります。

実際に作成してみましょう。class_cm.py ファイルを開き、以下のコードをコピーしてください。

## クラスベースのコンテキストマネージャ:__enter__ および __exit__ メソッドの実装
class ContextManager:
    def __enter__(self, *args, **kwargs):  ## 'with' ブロックに入るときに呼び出される
        print("--enter--")
        return self  ## 'as' 変数として使用するオブジェクトを返すことができる

    def __exit__(self, *args):  ## 'with' ブロックを終了するときに呼び出される
        print("--exit--")

with ContextManager():  ## __enter__ を呼び出し、完了したら __exit__ を呼び出す
    print("test")

ターミナルからスクリプトを実行します。

python3 class_cm.py

出力は制御の流れを示しています。まず __enter__ メソッドが呼び出され、次に with ブロック内のコードが実行され、最後にクリーンアップのために __exit__ メソッドが呼び出されます。

--enter--
test
--exit--

クラスベースのアプローチは、複雑なセットアップおよびティアダウンロジックを単一の再利用可能なオブジェクト内にカプセル化できるため強力です。

まとめ

この実験(Lab)の完了おめでとうございます!Python でコンテキストマネージャを使用してリソースを効果的に管理する方法を学びました。

まず、安全なファイル操作のための一般的な with ステートメントから始めました。次に、2 つの異なる方法で独自のカスタムコンテキストマネージャを作成する方法に進みました。

  • @contextlib.contextmanager デコレータを使用した関数ベースのアプローチ。これは、単純なセットアップおよびティアダウンタスクに最適です。
  • __enter__ および __exit__ メソッドを実装するクラスベースのアプローチ。これは、より複雑なリソース管理に理想的です。

これらのスキルは、リソースを適切に処理する、クリーンで信頼性が高く効率的な Python コードを書くために不可欠です。