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

У меня есть объект с подключением к внутренней базе данных, который активен на протяжении всего срока его службы. В конце работы программы соединение должно быть зафиксировано и закрыто. До сих пор я использовал явный метод close, но это несколько громоздко, особенно когда в вызывающем коде могут возникать исключения.

Я подумываю использовать метод __del__ для закрытия, но после некоторого чтения в Интернете у меня возникли опасения. Это допустимый образец использования? Могу ли я быть уверен, что внутренние ресурсы будут освобождены в __del__ правильно?

В ходе этого обсуждения возник аналогичный вопрос, но он не нашел удовлетворительного отвечать. Я не хочу иметь явный close метод, и использование with не является вариантом, потому что мой объект не используется так же просто, как открытие-воспроизведение-закрытие, но сохраняется как член другого, более крупного объекта, который использует его при работе в графическом интерфейсе.

В C ++ есть прекрасно работающие деструкторы, в которых можно безопасно освобождать ресурсы, поэтому я полагаю, что у Python тоже есть кое-что согласованное. По какой-то причине кажется, что это не так, и многие в сообществе клянутся против __del__. Какая же тогда альтернатива?


person Eli Bendersky    schedule 10.06.2009    source источник


Ответы (2)


Вы можете создать модуль подключения, поскольку модули сохраняют один и тот же объект во всем приложении, и зарегистрировать функцию для его закрытия с помощью _ 1_ модуль

# 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 ...")

Это предложение основано на предположении, что соединение в вашем приложении выглядит как один экземпляр (синглтон), который хорошо предоставляет глобальный модуль.

Если это не так, вы можете использовать деструктор.

Однако деструкторы плохо сочетаются со сборщиками мусора и циклическими ссылками (вы должны сами удалить циклическую ссылку перед вызовом деструктора), и если это не так (вам нужно несколько подключений), вы можете использовать деструктор. Только не держите круговые ссылки, иначе вам придется их сломать самостоятельно.

Кроме того, то, что вы сказали о C ++, неверно. Если вы используете деструкторы в C ++, они вызываются либо при завершении блока, определяющего объект (например, with в Python), либо при использовании ключевого слова delete (которое освобождает объект, созданный с помощью new). Вне этого вы должны использовать явный close(), который не является деструктором. Это похоже на python - python даже «лучше», потому что у него есть сборщик мусора.

person nosklo    schedule 10.06.2009
comment
Не является ли это неаккуратной «дырявой абстракцией» для объекта, использующего такое соединение? Почему я не могу просто использовать деструктор, как в C ++ ?? - person Eli Bendersky; 10.06.2009
comment
@eliben: может. Однако деструкторы плохо сочетаются со сборщиками мусора и циклическими ссылками (вы должны удалить циклическую ссылку самостоятельно перед вызовом деструктора), и соединение в вашем приложении выглядит как один экземпляр (синглтон), который хорошо предоставляет глобальный модуль. Если это не так (вам нужно несколько подключений), вы можете использовать деструктор. Только не держите круговые ссылки, иначе вам придется их сломать самостоятельно. - person nosklo; 10.06.2009
comment
re деструкторы C ++, не совсем так. При кодировании с правильным RAII указатель на ресурс будет храниться как интеллектуальный указатель (возможно, с подсчетом ссылок), который автоматически освобождается, когда счетчик достигает 0 гарантированным образом. Однако для этого также требуется дополнительное оборудование (умный указатель). - person Eli Bendersky; 10.06.2009
comment
@eliben: даже при использовании RAII ресурс освобождается и его деструктор вызывается только тогда, когда он выпадает из области видимости (например, with в Python). Если вы подключите какой-либо пакет подсчета ссылок, он будет вести себя точно так же, как деструктор python (т.е. у вас проблемы с циклическими ссылками). - person nosklo; 11.06.2009
comment
@nosklo: С RAII я создаю переменную экземпляра, которая является умным указателем. Он автоматически уничтожается, когда содержащиеся объекты выходят за пределы области видимости (когда все его переменные экземпляра уничтожаются), так что это не похоже на «with» в Python, IMHO. - person Eli Bendersky; 11.06.2009
comment
Я думаю, вам нужно проверить исключение, иначе if not con не сработает. - person Prof. Falken; 14.11.2012

Прочтите инструкцию with. Вы описываете его вариант использования.

Вам нужно будет заключить ваше соединение в класс «диспетчера контекста», который обрабатывает методы __enter__ и __exit__, используемые оператором with.

См. PEP 343 для получения дополнительной информации.


Изменить

«мой объект не используется так же просто, как« открыть-играть-закрыть », но сохраняется как член другого, более крупного объекта»

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()

Некоторые называют это «внедрением зависимостей» и «инверсией управления». Другие называют это шаблоном Стратегия. «ObjectThatMustBeClosed» - это стратегия, подключенная к более крупному объекту. Сборка создается на верхнем уровне приложения с графическим пользовательским интерфейсом, так как обычно именно там приобретаются ресурсы, такие как базы данных.

person S.Lott    schedule 10.06.2009
comment
Я упомянул, что не могу использовать здесь «с», и объяснил, почему, в самом вопросе. Я что-то упускаю? Можете ли вы объяснить, как можно эффективно использовать «with», когда объект хранится как экземпляр в большом классе на основе графического интерфейса пользователя, который работает на основе событий? - person Eli Bendersky; 10.06.2009
comment
@ S.Lott: Может быть, это ТАКАЯ ошибка? В моем вопросе 4 абзаца - в третьем упоминается «с» и графический интерфейс. - person Eli Bendersky; 10.06.2009
comment
Хотя ваше решение оригинально, я считаю, что ваше решение несколько излишне. Представьте, что у меня есть 10 таких объектов, которые нужно закрыть ... Все, что мне нужно, это деструктор !! - person Eli Bendersky; 11.06.2009
comment
@eliben: Возможно, ваша обертка для этих 10 объектов должна быть чем-то вроде контейнера? - person S.Lott; 11.06.2009