2009-06-10 4 views
6

Ich habe ein Objekt mit einer internen Datenbankverbindung, die während ihrer gesamten Lebensdauer aktiv ist. Am Ende des Programmlaufs muss die Verbindung festgeschrieben und geschlossen werden. Bisher habe ich eine explizite close Methode verwendet, aber das ist etwas umständlich, vor allem, wenn Ausnahmen im aufrufenden Code passieren können.Bereinigen einer internen Pysqlite-Verbindung bei Objektzerstörung

Ich erwäge die Verwendung der __del__ Methode zum Schließen, aber nach einigem Lesen online habe ich Bedenken. Ist das ein gültiges Nutzungsmuster? Kann ich sicher sein, dass die internen Ressourcen in __del__ korrekt freigegeben werden?

This discussion ausgelöst eine ähnliche Frage, aber keine befriedigende Antwort gefunden. Ich möchte keine explizite close Methode haben, und die Verwendung von with ist keine Option, weil mein Objekt nicht so einfach wie Open-Play-Close verwendet wird, sondern als Mitglied eines anderen, größeren Objekts, das gehalten wird verwendet es beim Ausführen in einer GUI.

C++ hat perfekt funktionierende Destruktoren, wo man Ressourcen sicher freisetzen kann, also würde ich mir vorstellen, dass Python auch etwas vereinbart hat. Aus irgendeinem Grund scheint es nicht der Fall zu sein, und viele in der Gemeinde schwören gegen __del__. Was ist dann die Alternative?

Antwort

5

Sie können ein Verbindungsmodul machen, da Module das gleiche Objekt in der gesamten Anwendung zu halten, und eine Funktion registrieren es mit dem atexit Modul zu schließen

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

Diese sugestion auf der Annahme beruht, dass die Verbindung in Ihrer Anwendung scheint wie eine einzelne Instanz (Singleton), die ein Modul global gut bietet.

Wenn das nicht der Fall ist, können Sie einen Destruktor verwenden.

Aber Destruktoren gehen nicht gut mit Garbage Collectors und Zirkelverweisen (Sie müssen den Zirkelverweis selbst entfernen, bevor der Destruktor aufgerufen wird) und wenn das nicht der Fall ist (Sie benötigen mehrere Verbindungen), können Sie einen Destruktor verwenden . Behalte einfach keine zirkulären Referenzen bei, sonst musst du sie selbst brechen.

Auch was Sie über C++ sagten, ist falsch. Wenn Sie Destruktoren in C++ verwenden, werden sie entweder aufgerufen, wenn der Block, der das Objekt definiert (wie Pythons with) oder wenn Sie das Schlüsselwort delete verwenden (das ein mit new erstelltes Objekt freigibt). Draußen müssen Sie eine explizite close() verwenden, die nicht der Destruktor ist. Es ist also genau wie Python - Python ist sogar "besser", weil es einen Garbage Collector hat.

+0

Ist diese Art von Unordentlichkeit in einer "undichten Abstraktion" Weise für das Objekt nicht, das solch eine Verbindung verwendet? Warum kann ich nicht einfach einen Destruktor wie in C++ verwenden? –

+0

@eliben: es kann. Destructors gehen jedoch nicht gut mit Garbage Collectors und Zirkelverweisen (Sie müssen den Zirkelverweis selbst entfernen, bevor der Destruktor aufgerufen wird) und die Verbindung in Ihrer Anwendung scheint wie eine einzelne Instanz (Singleton), die ein Modul global bietet. Wenn das nicht der Fall ist (Sie benötigen mehrere Verbindungen), können Sie einen Destruktor verwenden. Behalte einfach keine zirkulären Referenzen bei, sonst musst du sie selbst brechen. – nosklo

+0

re C++ Destruktoren, nicht genau. Bei Codierung mit korrekter RAII würde der Zeiger auf die Ressource als ein intelligenter Zeiger (vielleicht als Referenz gezählt) gehalten werden, der selbst freigegeben wird, wenn die Zählung auf eine garantierte Weise 0 erreicht. Dies erfordert jedoch zusätzliche Maschinen (den Smart Pointer) sowie –

6

Lesen Sie auf der with Anweisung. Sie beschreiben seinen Anwendungsfall.

Sie müssen Ihre Verbindung in eine "Context Manager" -Klasse einbetten, die die Methoden __enter__ und __exit__ verarbeitet, die von der with-Anweisung verwendet werden.

Weitere Informationen finden Sie unter PEP 343.


bearbeiten

"mein Ziel ist nicht so einfach wie Open-Play-close verwendet, sondern als Mitglied einer anderen, größeren Gegenstand gehalten"

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

Some Leute nennen das "Dependency Injection" und "Inversion of Control". Andere Leute nennen dies die Strategie Muster. Die "ObjectThatMustBeClosed" ist eine Strategie, die an ein größeres Objekt angeschlossen wird. Die Assembly wird auf der obersten Ebene der GUI-App erstellt, da normalerweise Ressourcen wie Datenbanken erfasst werden.

+0

Ich habe erwähnt, dass ich hier nicht 'mit' verwenden kann, und erklärte, warum, in der Frage selbst. Fehle ich etwas? Können Sie erklären, wie 'mit' effektiv angewendet werden kann, wenn das Objekt als Instanz in einer großen GUI-basierten Klasse gehalten wird, die auf Basis von Ereignissen funktioniert? –

+0

@ S.Lott: Könnte es ein SO-Bug sein? Meine Frage hat 4 Absätze - die dritte erwähnt "mit" und GUI –

+0

Während Original, ich fühle, dass Ihre Lösung ist etwas ein Overkill. Stell dir vor, dass ich 10 solcher Objekte habe, die geschlossen werden müssen ... Alles was ich brauchte, war ein Destruktor !! –