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

2677 ワード

コンテキストマネージャとは?
コードの環境はコンテキストであり,コンテキストマネージャプロトコルを実現したクラスの生成例がコンテキストマネージャオブジェクトである.クラス内の名前enterメソッドとexitメソッドの就き時にコンテキストマネージャプロトコルが実装されます.すなわち、この2つのメソッドが定義されている限り、このクラスのインスタンスはコンテキストマネージャオブジェクトです.
データベースへの適用
コンテキストマネージャオブジェクトが適用されない場合、私たちは一般的に接続データベース、sql操作、データの読み取り、接続の切断という順序で符号化を行いますが、接続データベースと接続の切断という2つの操作はすべての操作でしなければならないことなので、sqlが大量にある場合、このような論理的なコードによって大量の冗長コードが発生します.まず、最も原始的な書き方を見てみましょう.
class Database():
    def __init__(self):
        self.connected = False

    def connect(self):
        self.connected = True

    def close(self):
        self.connected = False

    def query(self):
        if self.connected:
            return "query data"
        else:
            raise ValueError("DB not connected.")

def handle_query():
    db = Database()
    db.connect()
    print("handling......", db.query())
    db.close()

上記のコードでは、データベースに接続されているかどうかを示すconnectedプロパティを定義するデータベースクラスDatabaseが作成されています.接続メソッドは、データベースへの接続操作を表します.closeメソッドは、データベース接続を閉じることを表します.queryメソッドはクエリー操作を表します.これは最も考えやすく、最も基礎的な操作論理でもある.
ジェネレータもこの問題を解決できます
ジェネレータは本質的に、あなたが書いた関数にインタフェースをセットしたような方法です.簡単な例を見てみましょう
def dbconn(fn):
    def wrapper(*args, **kwargs):
        db = Database()
        db.connect()
        ret = fn(db, *args, **kwargs)
        db.close()
        return ret
    return wrapper

@dbconn
def handle_query(db=None):
    print("handle---", db.query())

withこの問題をより優雅に解決できる
class Database():
    def __init__(self):
        self.connected = False

    def connect(self):
        self.connected = True

    def close(self):
        self.connected = False

    def query(self):
        if self.connected:
            return "query data"
        else:
            raise ValueError("DB not connected.")

    def __enter__(self):
        self.connect()
        return self

    def __exit__(self, exc_type, exc_val, exc_tb):
        self.close()

#   with   
def handle_query():
    with Database() as db:
        print("handle...", db.query())

#   handle_query()
handle_query()

Databaseというクラスにはenterとexitメソッドが含まれていることが明らかになった.これにより、Database()はコンテキストマネージャプロトコルを実装したオブジェクトを生成し、with文を使用することができます.with文は接続と切断を自動的に処理します.
with文はどうして自動的に処理されますか?
もちろんコードを使って制御する必要があります.まずwith Database()as db:このコードのdbはenterメソッドの戻り値です.また、この文を実行するときにコードがenterメソッドを先に実行し、データベースに接続する操作を行います.そしてprint(「handle...」を実行し、db.query()というコードは、クエリーsqlの処理を表す.print文(つまりクエリー文)が実行されると、exitメソッドがコールバックされ、ここにデータベース接続を閉じるコードが書き込まれます.これにより、無制限に接続と切断されたコードを書く必要はありません.完璧に解決する.
このexitメソッドは、with文の関数体に異常が発生した場合にeixtメソッドも呼び出されるため、データベースの接続が切断されるため、安全です.