2009-06-10 5 views
6

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

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

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

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

ответ

5

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

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

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

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

+0

Разве это не так-то неясно, как «непротекающая абстракция» для объекта, использующего такое соединение? Почему я не могу использовать деструктор, как в C++? –

+0

@eliben: можно. Однако деструкторы не справляются с сборщиками мусора и круговыми ссылками (вы должны удалить кольцевую ссылку самостоятельно до вызова деструктора), а соединение в вашем приложении похоже на один экземпляр (singleton), который обеспечивает глобальный модуль. Если это не так (вам нужно несколько соединений), вы можете пойти деструктором. Просто не держите круговые ссылки, или вам придется их самостоятельно разбить. – nosklo

+0

деструкторов re C++, не совсем. Кодирование с правильным RAII, указатель на ресурс будет храниться в виде умного указателя (возможно, подсчета ссылок), который освобождается сам, когда счетчик достигает 0 в гарантированном порядке. Однако для этого требуется дополнительное оборудование (умный указатель) –

6

Читайте в сообщении with. Вы описываете его вариант использования.

Вам необходимо обернуть ваше соединение в классе «Контекстный менеджер», который обрабатывает методы и __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() 

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

+0

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

+0

@ S.Lott: Это может быть ошибка SO? Мой вопрос имеет 4 абзаца - третий упоминает «с» и GUI –

+0

Несмотря на оригинальность, я чувствую, что ваше решение несколько перебор. Представьте, что у меня есть 10 таких объектов, которые нужно закрыть ... Все, что мне нужно, было деструктором! –