Au lieu de demander quelle est la pratique standard, car elle est souvent peu claire et subjective, vous pouvez essayer de consulter le module lui-même pour obtenir des conseils. En général, utiliser le withmot - clé comme un autre utilisateur l'a suggéré est une excellente idée, mais dans ce cas précis, il peut ne pas vous donner tout à fait les fonctionnalités que vous attendez.
Depuis la version 1.2.5 du module, MySQLdb.Connectionimplémente le protocole du gestionnaire de contexte avec le code suivant ( 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()
Il existe déjà plusieurs questions / réponses with, ou vous pouvez lire l'instruction «with» de Python , mais ce qui se passe essentiellement, c'est qu'elle __enter__s'exécute au début du withbloc et __exit__s'exécute en quittant le withbloc. Vous pouvez utiliser la syntaxe facultative with EXPR as VARpour lier l'objet renvoyé par __enter__à un nom si vous prévoyez de référencer cet objet ultérieurement. Donc, étant donné l'implémentation ci-dessus, voici un moyen simple d'interroger votre base de données:
connection = MySQLdb.connect(...)
with connection as cursor:
cursor.execute('select 1;')
result = cursor.fetchall()
print result
La question est maintenant, quels sont les états de la connexion et du curseur après avoir quitté le withbloc? La __exit__méthode présentée ci-dessus appelle uniquement self.rollback()ou self.commit(), et aucune de ces méthodes n'appelle la close()méthode. Le curseur lui-même n'a pas de __exit__méthode définie - et n'aurait pas d'importance s'il le faisait, car il withne gère que la connexion. Par conséquent, la connexion et le curseur restent ouverts après la sortie du withbloc. Ceci est facilement confirmé en ajoutant le code suivant à l'exemple ci-dessus:
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'
Vous devriez voir la sortie "le curseur est ouvert; la connexion est ouverte" imprimée sur stdout.
Je pense que vous devez fermer le curseur avant de valider la connexion.
Pourquoi? L' API C MySQL , qui en est la base MySQLdb, n'implémente aucun objet curseur, comme le suggère la documentation du module: "MySQL ne prend pas en charge les curseurs; cependant, les curseurs sont facilement émulés." En effet, la MySQLdb.cursors.BaseCursorclasse hérite directement des objectcurseurs et n'impose aucune restriction de ce type en ce qui concerne la validation / la restauration. Un développeur Oracle avait ceci à dire :
cnx.commit () avant cur.close () me semble le plus logique. Peut-être pouvez-vous suivre la règle: "Fermez le curseur si vous n'en avez plus besoin." Ainsi commit () avant de fermer le curseur. En fin de compte, pour Connector / Python, cela ne fait pas beaucoup de différence, mais ou pour d'autres bases de données, cela pourrait.
Je pense que c'est aussi proche que vous allez arriver à la "pratique standard" sur ce sujet.
Y a-t-il un avantage significatif à trouver des ensembles de transactions qui ne nécessitent pas de validations intermédiaires afin que vous n'ayez pas à obtenir de nouveaux curseurs pour chaque transaction?
J'en doute beaucoup, et en essayant de le faire, vous risquez d'introduire une erreur humaine supplémentaire. Mieux vaut décider d'une convention et s'y tenir.
Y a-t-il beaucoup de frais généraux pour obtenir de nouveaux curseurs, ou n'est-ce pas un problème?
La surcharge est négligeable et ne touche pas du tout le serveur de base de données; c'est entièrement dans l'implémentation de MySQLdb. Vous pouvez regarder BaseCursor.__init__sur github si vous êtes vraiment curieux de savoir ce qui se passe lorsque vous créez un nouveau curseur.
En revenant à plus tôt lorsque nous discutions with, vous pouvez peut-être maintenant comprendre pourquoi la MySQLdb.Connectionclasse __enter__et les __exit__méthodes vous donnent un tout nouvel objet curseur dans chaque withbloc et ne prennent pas la peine de le suivre ou de le fermer à la fin du bloc. Il est assez léger et existe uniquement pour votre commodité.
S'il est vraiment important pour vous de microgérer l'objet curseur, vous pouvez utiliser contextlib.closing pour compenser le fait que l'objet curseur n'a pas de __exit__méthode définie . Pour cette question, vous pouvez également l'utiliser pour forcer l'objet de connexion à se fermer à la sortie d'un withbloc. Cela devrait afficher "my_curs est fermé; my_conn est fermé":
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'
Notez que with closing(arg_obj)cela n'appellera pas les méthodes __enter__et de l'objet argument __exit__; il n'appel de l'objet de l' argument de la méthode à la fin du bloc. (Pour voir en action, définir simplement une classe avec , et des méthodes simples contenant des déclarations, et comparer ce qui se passe quand vous faites à ce qui se passe quand vous faites .) Cela a deux conséquences importantes:closewithFoo__enter____exit__closeprintwith Foo(): passwith closing(Foo()): pass
Premièrement, si le mode autocommit est activé, MySQLdb effectuera BEGINune transaction explicite sur le serveur lorsque vous utiliserez with connectionet validerez ou annulerez la transaction à la fin du bloc. Ce sont des comportements par défaut de MySQLdb, destinés à vous protéger du comportement par défaut de MySQL consistant à valider immédiatement toutes les instructions DML. MySQLdb suppose que lorsque vous utilisez un gestionnaire de contexte, vous souhaitez une transaction et utilise l'explicite BEGINpour contourner le paramètre de validation automatique sur le serveur. Si vous avez l'habitude d'utiliser with connection, vous pourriez penser que l'autocommit est désactivé alors qu'en réalité il était seulement contourné. Vous pourriez avoir une mauvaise surprise si vous ajoutezclosingà votre code et perdez l'intégrité transactionnelle; vous ne pourrez pas annuler les modifications, vous pouvez commencer à voir des bogues de concurrence et pourquoi ne pas être immédiatement évident.
Deuxièmement, with closing(MySQLdb.connect(user, pass)) as VARlie l' objet de connexion à VAR, contrairement à with MySQLdb.connect(user, pass) as VAR, qui lie un nouvel objet curseur à VAR. Dans ce dernier cas, vous n'auriez pas d'accès direct à l'objet de connexion! Au lieu de cela, vous devrez utiliser l' connectionattribut du curseur , qui fournit un accès proxy à la connexion d'origine. Lorsque le curseur est fermé, son connectionattribut est défini sur None. Cela entraîne une connexion abandonnée qui restera jusqu'à ce que l'un des événements suivants se produise:
- Toutes les références au curseur sont supprimées
- Le curseur sort de la portée
- La connexion expire
- La connexion est fermée manuellement via les outils d'administration du serveur
Vous pouvez tester cela en surveillant les connexions ouvertes (dans Workbench ou en utilisantSHOW PROCESSLIST ) tout en exécutant les lignes suivantes une par une:
with MySQLdb.connect(...) as my_curs:
pass
my_curs.close()
my_curs.connection
my_curs.connection.close()
del my_curs