標準的な方法を尋ねる代わりに、それはしばしば不明確で主観的なものであるため、モジュール自体を調べてガイダンスを求めることができます。一般に、with
を使用します 別のユーザーが提案したキーワードは素晴らしいアイデアですが、この特定の状況では、期待する機能が十分に得られない場合があります。
モジュールのバージョン1.2.5以降、MySQLdb.Connection
コンテキストマネージャープロトコル
を実装します 次のコードを使用します( github
):
def __enter__(self):
if self.get_autocommit():
self.query("BEGIN")
return self.cursor()
def __exit__(self, exc, value, tb):
if exc:
self.rollback()
else:
self.commit()
with
に関する既存のQ&Aがいくつかあります すでに、またはPythonの「with」ステートメントを理解する
を読むことができます 、しかし本質的に何が起こるかはその__enter__
with
の先頭で実行されます ブロック、および__exit__
with
を離れると実行されます ブロック。オプションの構文with EXPR as VAR
を使用できます __enter__
によって返されたオブジェクトをバインドします 後でそのオブジェクトを参照する場合は、名前に変更します。したがって、上記の実装を前提として、データベースにクエリを実行する簡単な方法は次のとおりです。
connection = MySQLdb.connect(...)
with connection as cursor: # connection.__enter__ executes at this line
cursor.execute('select 1;')
result = cursor.fetchall() # connection.__exit__ executes after this line
print result # prints "((1L,),)"
ここで問題となるのは、with
を終了した後の接続とカーソルの状態はどうなっているのかということです。 ブロック? __exit__
上記のメソッドはself.rollback()
のみを呼び出します またはself.commit()
、およびこれらのメソッドはどちらもclose()
を呼び出しません。 方法。カーソル自体には__exit__
はありません メソッドが定義されています– with
であるため、定義されているかどうかは関係ありません 接続を管理しているだけです。したがって、with
を終了した後も、接続とカーソルの両方が開いたままになります。 ブロック。これは、上記の例に次のコードを追加することで簡単に確認できます。
try:
cursor.execute('select 1;')
print 'cursor is open;',
except MySQLdb.ProgrammingError:
print 'cursor is closed;',
if connection.open:
print 'connection is open'
else:
print 'connection is closed'
stdoutに「カーソルが開いています。接続が開いています」という出力が出力されます。
接続を確定する前にカーソルを閉じる必要があると思います。
なんで? MySQL C API
、これはMySQLdb
の基礎です 、モジュールのドキュメントに示されているように、カーソルオブジェクトを実装しません:" MySQLカーソルはサポートされていませんが、カーソルは簡単にエミュレートされます。」
実際、MySQLdb.cursors.BaseCursor
クラスはobject
から直接継承します コミット/ロールバックに関してカーソルにそのような制限を課しません。 Oracle開発者
cur.close()の前のcnx.commit()は、私にとって最も論理的に聞こえます。たぶん、「もう必要ない場合はカーソルを閉じる」というルールに従うことができます。したがって、カーソルを閉じる前にcommit()を実行します。結局のところ、Connector / Pythonの場合、大きな違いはありませんが、他のデータベースでは大きな違いがあります。
これは、このテーマに関する「標準的な実践」に到達するのと同じくらい近いと思います。
トランザクションごとに新しいカーソルを取得する必要がないように、中間コミットを必要としないトランザクションのセットを見つけることに大きな利点はありますか?
私はそれを非常に疑っています、そしてそうしようとすると、あなたは追加のヒューマンエラーを導入するかもしれません。大会を決めてそれを守るほうがいいです。
新しいカーソルを取得するためのオーバーヘッドはたくさんありますか、それとも大したことではありませんか?
オーバーヘッドはごくわずかであり、データベースサーバーにはまったく影響しません。それは完全にMySQLdbの実装内にあります。 BaseCursor.__init__
を見ることができます githubで
新しいカーソルを作成したときに何が起こっているのかを知りたい場合は、
with
について話し合っていた以前の話に戻ります 、おそらく今、あなたはMySQLdb.Connection
の理由を理解することができます クラス__enter__
および__exit__
メソッドは、すべてのwith
でまったく新しいカーソルオブジェクトを提供します ブロックし、それを追跡したり、ブロックの最後で閉じたりしないでください。それはかなり軽量で、純粋にあなたの便宜のために存在します。
カーソルオブジェクトを細かく管理することが本当に重要な場合は、 contextlib.closing
カーソルオブジェクトに定義された__exit__
がないという事実を補うため 方法。さらに言えば、これを使用して、with
を終了するときに接続オブジェクトを強制的に閉じることもできます。 ブロック。これにより、「my_cursは閉じています。my_connは閉じています」と出力されます:
from contextlib import closing
import MySQLdb
with closing(MySQLdb.connect(...)) as my_conn:
with closing(my_conn.cursor()) as my_curs:
my_curs.execute('select 1;')
result = my_curs.fetchall()
try:
my_curs.execute('select 1;')
print 'my_curs is open;',
except MySQLdb.ProgrammingError:
print 'my_curs is closed;',
if my_conn.open:
print 'my_conn is open'
else:
print 'my_conn is closed'
with closing(arg_obj)
に注意してください 引数オブジェクトの__enter__
は呼び出されません および__exit__
メソッド; のみ 引数オブジェクトのclose
を呼び出します with
の最後にあるメソッド ブロック。 (これが実際に動作していることを確認するには、クラスFoo
を定義するだけです。 __enter__
を使用 、__exit__
、およびclose
単純なprint
を含むメソッド ステートメントを作成し、with Foo(): pass
を実行するとどうなるかを比較します。 with closing(Foo()): pass
。)これには2つの重要な意味があります:
まず、自動コミットモードが有効になっている場合、MySQLdbはBEGIN
with connection
を使用する場合のサーバーでの明示的なトランザクション ブロックの最後でトランザクションをコミットまたはロールバックします。これらはMySQLdbのデフォルトの動作であり、すべてのDMLステートメントを即座にコミットするというMySQLのデフォルトの動作からユーザーを保護することを目的としています。 MySQLdbは、コンテキストマネージャーを使用するときにトランザクションが必要であると想定し、明示的なBEGIN
を使用します サーバーの自動コミット設定をバイパスします。 with connection
の使用に慣れている場合 、実際にはバイパスされているだけの場合、自動コミットは無効になっていると思うかもしれません。 closing
を追加すると、不快な驚きを感じるかもしれません。 コードに影響を与え、トランザクションの整合性を失います。変更をロールバックできなくなり、同時実行のバグが発生し始め、その理由がすぐにはわからない場合があります。
次に、with closing(MySQLdb.connect(user, pass)) as VAR
接続オブジェクトをバインドします VAR
へ 、with MySQLdb.connect(user, pass) as VAR
とは対照的です 、新しいカーソルオブジェクトをバインドします VAR
へ 。後者の場合、接続オブジェクトに直接アクセスすることはできません。代わりに、カーソルのconnection
を使用する必要があります 元の接続へのプロキシアクセスを提供する属性。カーソルを閉じると、そのconnection
属性がNone
に設定されている 。これにより、接続が切断され、次のいずれかが発生するまで接続が維持されます。
- カーソルへのすべての参照が削除されます
- カーソルがスコープ外になります
- 接続がタイムアウトします
- サーバー管理ツールを使用して接続を手動で閉じます
これをテストするには、開いている接続を監視します(Workbenchまたは SHOW PROCESSLIST
を使用する
)次の行を1つずつ実行している間:
with MySQLdb.connect(...) as my_curs:
pass
my_curs.close()
my_curs.connection # None
my_curs.connection.close() # throws AttributeError, but connection still open
del my_curs # connection will close here