Anstatt zu fragen, was Standardpraxis ist, da dies oft unklar und subjektiv ist, können Sie versuchen, das Modul selbst als Anleitung zu betrachten. Im Allgemeinen ist die Verwendung des withSchlüsselworts als Vorschlag eines anderen Benutzers eine gute Idee, aber unter diesen besonderen Umständen erhalten Sie möglicherweise nicht die erwartete Funktionalität.
MySQLdb.ConnectionImplementiert ab Version 1.2.5 des Moduls das Kontextmanagerprotokoll mit dem folgenden Code ( 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()
Es gibt bereits mehrere Fragen und withAntworten, oder Sie können die "with" -Anweisung von Understanding Python lesen. Im Wesentlichen wird diese jedoch __enter__am Anfang des withBlocks und __exit__beim Verlassen des withBlocks ausgeführt. Sie können die optionale Syntax verwenden with EXPR as VAR, um das von zurückgegebene Objekt __enter__an einen Namen zu binden, wenn Sie später auf dieses Objekt verweisen möchten. Angesichts der obigen Implementierung haben Sie hier eine einfache Möglichkeit, Ihre Datenbank abzufragen:
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,),)"
Die Frage ist nun, wie sind die Zustände der Verbindung und des Cursors nach dem Verlassen des withBlocks? Die __exit__oben gezeigte Methode ruft nur self.rollback()oder auf self.commit(), und keine dieser Methoden ruft die close()Methode auf. Für den Cursor selbst ist keine __exit__Methode definiert - und es wäre egal, ob dies der Fall ist, da withnur die Verbindung verwaltet wird. Daher bleiben sowohl die Verbindung als auch der Cursor nach dem Verlassen des withBlocks geöffnet . Dies kann leicht bestätigt werden, indem dem obigen Beispiel der folgende Code hinzugefügt wird:
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'
Sie sollten sehen, dass die Ausgabe "Cursor ist offen; Verbindung ist offen" auf stdout gedruckt wird.
Ich glaube, Sie müssen den Cursor schließen, bevor Sie die Verbindung herstellen.
Warum? Die MySQL C-API , auf der die Basis basiert MySQLdb, implementiert kein Cursorobjekt, wie in der Moduldokumentation impliziert: "MySQL unterstützt keine Cursor; Cursor können jedoch leicht emuliert werden." In der Tat MySQLdb.cursors.BaseCursorerbt die Klasse direkt von objectCursorn und legt keine solche Einschränkung in Bezug auf Commit / Rollback fest. Ein Oracle-Entwickler hatte folgendes zu sagen :
cnx.commit () vor cur.close () klingt für mich am logischsten. Vielleicht können Sie sich an die Regel halten: "Schließen Sie den Cursor, wenn Sie ihn nicht mehr benötigen." Legen Sie also commit () fest, bevor Sie den Cursor schließen. Letztendlich macht es für Connector / Python keinen großen Unterschied, aber für andere Datenbanken.
Ich gehe davon aus, dass dies so nah wie möglich an der "Standardpraxis" zu diesem Thema liegt.
Gibt es einen signifikanten Vorteil beim Finden von Transaktionssätzen, für die keine Zwischen-Commits erforderlich sind, damit Sie nicht für jede Transaktion neue Cursor erhalten müssen?
Ich bezweifle es sehr, und wenn Sie dies versuchen, können Sie zusätzliche menschliche Fehler einführen. Entscheiden Sie sich besser für eine Konvention und bleiben Sie dabei.
Gibt es viel Aufwand, um neue Cursor zu bekommen, oder ist es einfach keine große Sache?
Der Overhead ist vernachlässigbar und berührt den Datenbankserver überhaupt nicht. Es liegt vollständig in der Implementierung von MySQLdb. Sie können sich BaseCursor.__init__Github ansehen, wenn Sie wirklich neugierig sind, was passiert, wenn Sie einen neuen Cursor erstellen.
Wenn Sie zu einem früheren Zeitpunkt zurückkehren, als wir darüber gesprochen haben with, können Sie jetzt vielleicht verstehen, warum die MySQLdb.ConnectionKlasse __enter__und die __exit__Methoden Ihnen in jedem withBlock ein brandneues Cursorobjekt geben, und Sie müssen sich nicht die Mühe machen, es zu verfolgen oder am Ende des Blocks zu schließen. Es ist ziemlich leicht und existiert nur für Ihre Bequemlichkeit.
Wenn es für Sie wirklich so wichtig ist, das Cursorobjekt zu verwalten, können Sie contextlib.closing verwenden , um die Tatsache auszugleichen , dass das Cursorobjekt keine definierte __exit__Methode hat. Sie können es auch verwenden, um das Verbindungsobjekt zu zwingen, sich beim Verlassen eines withBlocks selbst zu schließen . Dies sollte "my_curs ist geschlossen; my_conn ist geschlossen" ausgeben:
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'
Beachten Sie, dass with closing(arg_obj)die Argumente __enter__und __exit__Methoden des Argumentobjekts nicht aufgerufen werden. Die Methode des Argumentobjekts wird nurclose am Ende des withBlocks aufgerufen . (Um dies in Aktion zu sehen, definieren Sie einfach eine Klasse Foomit __enter__, __exit__und closeMethoden, die einfache printAnweisungen enthalten, und vergleichen Sie, was passiert, wenn Sie es tun, with Foo(): passmit dem, was passiert, wenn Sie es tun with closing(Foo()): pass.) Dies hat zwei wesentliche Auswirkungen:
Wenn der Autocommit-Modus aktiviert ist, führt MySQLdb zunächst BEGINeine explizite Transaktion auf dem Server durch, wenn Sie with connectiondie Transaktion am Ende des Blocks verwenden und festschreiben oder zurücksetzen . Dies sind Standardverhalten von MySQLdb, das Sie vor dem Standardverhalten von MySQL schützen soll, bei dem alle DML-Anweisungen sofort festgeschrieben werden. MySQLdb geht davon aus, dass Sie bei Verwendung eines Kontextmanagers eine Transaktion wünschen, und verwendet die explizite Option BEGIN, um die Autocommit-Einstellung auf dem Server zu umgehen. Wenn Sie es gewohnt sind with connection, denken Sie möglicherweise, dass Autocommit deaktiviert ist, wenn es tatsächlich nur umgangen wurde. Sie könnten eine unangenehme Überraschung bekommen, wenn Sie hinzufügenclosingzu Ihrem Code und verlieren die Transaktionsintegrität; Sie können Änderungen nicht rückgängig machen, es treten möglicherweise Parallelitätsfehler auf, und es ist möglicherweise nicht sofort ersichtlich, warum.
Zweitens with closing(MySQLdb.connect(user, pass)) as VARbindet das Verbindungsobjekt zu VAR, im Gegensatz zu with MySQLdb.connect(user, pass) as VAR, das bindet ein neues Cursor - Objekt zu VAR. Im letzteren Fall hätten Sie keinen direkten Zugriff auf das Verbindungsobjekt! Stattdessen müssten Sie das connectionAttribut des Cursors verwenden , das den Proxy-Zugriff auf die ursprüngliche Verbindung ermöglicht. Wenn der Cursor geschlossen ist, wird sein connectionAttribut auf gesetzt None. Dies führt zu einer abgebrochenen Verbindung, die so lange bestehen bleibt, bis eine der folgenden Situationen eintritt:
- Alle Verweise auf den Cursor werden entfernt
- Der Cursor verlässt den Bereich
- Die Verbindung läuft ab
- Die Verbindung wird manuell über Serververwaltungstools geschlossen
Sie können dies testen, indem Sie offene Verbindungen überwachen (in Workbench oder mithilfe vonSHOW PROCESSLIST ), während Sie die folgenden Zeilen einzeln ausführen:
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