Очистка внутреннего pysqlite соединения при уничтожении объекта

У меня есть объект с внутренним подключением к базе данных, который активен на протяжении всего его жизненного цикла. В конце запуска программы соединение должно быть зафиксировано и закрыто. До сих пор я использовал явный метод 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, так как обычно там, где приобретаются ресурсы, такие как базы данных.