MySQLdb Connections
имеет рудиментарный менеджер контекста, который создает курсор для ввода, либо откатывается, либо совершает на выходе, и неявно не подавляет исключения. Из Источник подключения:
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()
Итак, кто-нибудь знает, почему курсор не закрыт при выходе?
Сначала я предположил, что это было потому, что закрытие курсора ничего не делало, и что курсоры только имели близкий метод в знак уважения к API-интерфейс Python DB (см. комментарии к этому ответу). Однако факт заключается в том, что закрытие курсора сжигает оставшиеся наборы результатов, если таковые имеются, и отключает курсор. Из источника курсора:
def close(self):
"""Close the cursor. No further queries will be possible."""
if not self.connection: return
while self.nextset(): pass
self.connection = None
Было бы легко закрыть курсор при выходе, поэтому я должен предположить, что это не было сделано специально. С другой стороны, мы видим, что когда курсор удаляется, он все равно закрыт, поэтому я думаю, что сборщик мусора в конечном итоге обойдет его. Я мало знаю о сборке мусора в Python.
def __del__(self):
self.close()
self.errorhandler = None
self._result = None
Еще одна догадка заключается в том, что может возникнуть ситуация, когда вы хотите повторно использовать курсор после блока with
. Но я не могу придумать, почему вам нужно это делать. Не можете ли вы всегда использовать курсор внутри его контекста и просто использовать отдельный контекст для следующей транзакции?
Чтобы быть предельно ясным, этот пример, очевидно, не имеет смысла:
with conn as cursor:
cursor.execute(select_stmt)
rows = cursor.fetchall()
Это должно быть:
with conn as cursor:
cursor.execute(select_stmt)
rows = cursor.fetchall()
И этот пример не имеет смысла:
# first transaction
with conn as cursor:
cursor.execute(update_stmt_1)
# second transaction, reusing cursor
try:
cursor.execute(update_stmt_2)
except:
conn.rollback()
else:
conn.commit()
Это должно быть просто:
# first transaction
with conn as cursor:
cursor.execute(update_stmt_1)
# second transaction, new cursor
with conn as cursor:
cursor.execute(update_stmt_2)
Опять же, что может быть вредным для закрытия курсора при выходе, и какие преимущества там не для его закрытия?