У меня есть объект с внутренним подключением к базе данных, который активен на протяжении всего его жизненного цикла. В конце запуска программы соединение должно быть зафиксировано и закрыто. До сих пор я использовал явный метод close
, но это несколько громоздко, особенно если в вызывающем коде могут быть исключения.
Я рассматриваю возможность использования метода __del__
для закрытия, но после некоторого чтения в Интернете у меня есть проблемы. Является ли это допустимым шаблоном использования? Могу ли я быть уверенным, что внутренние ресурсы будут освобождены в __del__
правильно?
Эта дискуссия подняла аналогичный вопрос, но не нашла удовлетворительного ответа. Я не хочу иметь явный метод close
, а использование with
не является опцией, потому что мой объект не используется так же просто, как open-play-close, но сохраняется как член другого, более крупный объект, который использует его во время работы в графическом интерфейсе.
У С++ есть отлично работающие деструкторы, где можно безопасно освобождать ресурсы, поэтому я бы предположил, что у Python есть что-то согласованное. По какой-то причине это, похоже, не так, и многие в сообществе обет против __del__
. Какая альтернатива, тогда?
Ответ 1
Вы можете создать модуль подключения, так как модули сохраняют один и тот же объект во всем приложении и регистрируют функцию, чтобы закрыть ее с помощью atexit
модуль
# db.py:
import sqlite3
import atexit
con = None
def get_connection():
global con
if not con:
con = sqlite3.connect('somedb.sqlite')
atexit.register(close_connection, con)
return con
def close_connection(some_con):
some_con.commit()
some_con.close()
# your_program.py
import db
con = db.get_connection()
cur = con.cursor()
cur.execute("SELECT ...")
Это утверждение основано на предположении, что соединение в вашем приложении похоже на один экземпляр (singleton), который обеспечивает глобальный модуль.
Если это не так, вы можете использовать деструктор.
Однако деструкторы не справляются с сборщиками мусора и круговыми ссылками (вы должны удалить круговую ссылку самостоятельно до вызова деструктора), и если это не так (вам нужно несколько соединений), вы можете пойти на деструктор. Просто не держите круговые ссылки, или вам придется их самостоятельно разбить.
Кроме того, то, что вы сказали о С++, неверно. Если вы используете деструкторы в С++, они вызываются либо когда блок, который определяет объект, заканчивается (например, python with
), либо когда вы используете ключевое слово delete
(которое освобождает объект, созданный с помощью new
). Вне этого вы должны использовать явный close()
, который не является деструктором. Таким образом, это похоже на python - python даже "лучше", потому что у него есть сборщик мусора.
Ответ 2
Читайте в with. Вы описываете его вариант использования.
Вам нужно будет связать ваше соединение в классе "Контекстный менеджер", который обрабатывает методы __enter__
и __exit__
, используемые оператором with
.
Подробнее см. PEP 343.
Edit
"мой объект не используется так же просто, как open-play-close, но сохраняется как член другого более крупного объекта
class AnObjectWhichMustBeClosed( object ):
def __enter__( self ):
# acquire
def __exit__( self, type, value, traceback ):
# release
def open( self, dbConnectionInfo ):
# open the connection, updating the state for __exit__ to handle.
class ALargerObject( object ):
def __init__( self ):
pass
def injectTheObjectThatMustBeClosed( self, anObject ):
self.useThis = anObject
class MyGuiApp( self ):
def run( self ):
# build GUI objects
large = ALargeObject()
with AnObjectWhichMustBeClosed() as x:
large.injectTheObjectThatMustBeClosed( x )
mainLoop()
Некоторые люди называют это "Injection Dependency" и "Inversion of Control". Другие люди называют этот шаблон Стратегия. "ObjectThatMustBeClosed" - это стратегия, подключенная к более крупному объекту. Сборка создается на верхнем уровне приложения GUI, так как обычно там, где приобретаются ресурсы, такие как базы данных.