2009-06-10 7 views
6

Ho un oggetto con una connessione al database interno attiva per tutta la sua durata. Alla fine dell'esecuzione del programma, la connessione deve essere confermata e chiusa. Finora ho usato un metodo esplicito close, ma questo è piuttosto complicato, specialmente quando possono verificarsi eccezioni nel codice chiamante.Pulizia di una connessione interna di pysqlite sulla distruzione di oggetti

Sto valutando l'utilizzo del metodo __del__ per la chiusura, ma dopo alcune letture online ho dei dubbi. È un modello di utilizzo valido? Posso essere sicuro che le risorse interne saranno liberate in __del__ correttamente?

This discussion ha sollevato una domanda simile ma non ha trovato risposta soddisfacente. Non voglio avere un metodo esplicito close e usare with non è un'opzione, perché il mio oggetto non è usato semplicemente come open-play-close, ma è conservato come membro di un altro oggetto più grande, lo usa mentre è in esecuzione in una GUI.

C++ ha distruttori perfettamente funzionanti in cui è possibile liberare risorse in modo sicuro, quindi immagino che Python abbia qualcosa d'accordo. Per qualche motivo sembra non essere il caso, e molti nella comunità giurano contro __del__. Qual è l'alternativa, allora?

risposta

5

Si può fare un modulo di connessione, in quanto i moduli mantengono lo stesso oggetto in tutta l'applicazione, e registrare una funzione di chiudere con il modulo 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 ...") 

Questa sugestion si basa sul presupposto che la connessione nella tua applicazione sembra una singola istanza (singleton) che un modulo globale fornisce bene.

Se questo non è il caso, quindi è possibile utilizzare un distruttore.

Tuttavia i distruttori non funzionano bene con i raccoglitori di rifiuti e i riferimenti circolari (è necessario rimuovere il riferimento circolare da soli prima che venga chiamato il distruttore) e se ciò non è il caso (sono necessarie più connessioni), è possibile utilizzare un distruttore . Basta non tenere i riferimenti circolari in giro o dovrai romperli da solo.

Inoltre, quello che hai detto su C++ è sbagliato. Se si usano i distruttori in C++, vengono chiamati quando il blocco che definisce l'oggetto termina (come python with) o quando si utilizza la parola chiave delete (che rilascia un oggetto creato con new). Al di fuori di questo è necessario utilizzare un esplicito close() che non sia il distruttore. Quindi è proprio come python - python è anche "migliore" perché ha un garbage collector.

+0

Non è questo tipo di disordinato in un modo di "astrazione sfuggente" per l'oject che usa tale connessione? Perché non posso usare solo un distruttore come in C++ ?? –

+0

@eliben: può. Tuttavia i distruttori non vanno bene con i garbage collector ei riferimenti circolari (è necessario rimuovere il riferimento circolare da soli prima che venga chiamato il distruttore) e la connessione nell'applicazione sembra una singola istanza (singleton) che un modulo globale fornisce bene. Se questo non è il caso (hai bisogno di più connessioni) allora puoi andare per un distruttore. Basta non tenere i riferimenti circolari in giro o dovrai romperli da solo. – nosklo

+0

I distruttori di re C++, non esattamente. Codificando con RAII corretto, il puntatore alla risorsa verrebbe tenuto come un puntatore intelligente (magari conteggiato come riferimento) che viene deallocato automaticamente quando il conteggio raggiunge 0 in modo garantito. Tuttavia, ciò richiede un macchinario aggiuntivo (il puntatore intelligente) e anche –

6

Leggere l'istruzione with. Stai descrivendo il suo caso d'uso.

Avrete bisogno di avvolgere la connessione in una classe "Context Manager" che gestisce i metodi __enter__ e __exit__ utilizzati dall'istruzione with.

Vedere PEP 343 per ulteriori informazioni.


Modifica

"il mio scopo non è usato come semplicemente come open-play-vicino, ma viene mantenuto come un membro di un altro, più grande oggetto"

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

Alcuni la gente chiama questa "Iniezione di dipendenza" e "Inversione del controllo". Altre persone lo chiamano il modello Strategia. "ObjectThatMustBeClosed" è una strategia, inserita in un oggetto più grande. L'assembly viene creato a un livello superiore dell'app GUI, poiché di solito vengono acquisite risorse come i database.

+0

Ho menzionato che non posso usare 'con' qui, e spiegato perché, nella domanda stessa. Mi sto perdendo qualcosa? Puoi spiegare in che modo "with" può essere impiegato efficacemente quando l'oggetto viene considerato come un'istanza in una grande classe basata su GUI che funziona in base agli eventi? –

+0

@ S.Lott: Potrebbe essere un SO bug? La mia domanda ha 4 paragrafi - la terza menziona "con" e la GUI –

+0

Mentre originale, ritengo che la vostra soluzione sia in qualche modo eccessiva. Immagina di avere 10 di questi oggetti che devono essere chiusi ... Tutto ciò di cui avevo bisogno era un distruttore !! –

Problemi correlati