2012-01-18 14 views
6

Ho una classe che apre un file per la scrittura. Nel mio distruttore, che io chiamo la funzione che chiude il file:del MyClass non chiama oggetto .__ del __()

class MyClass: 
    def __del__(self): 
     self.close() 

    def close(self): 
     if self.__fileHandle__ is not None: 
       self.__fileHandle__.close() 

ma quando elimino l'oggetto con codice come:

myobj = MyClass() 
myobj.open() 
del myobj 

se provo a reinstanziare l'oggetto, ottengo un errore di valore :

ValueError: The file 'filename' is already opened. Please close it before reopening in write mode. 

mentre se chiamo myobj.close() prima del myobj non capisco questo problema. Quindi, perché non viene chiamato __del__()?

+1

Provare ad ereditare da 'object' - non farlo è stato considerato obsoleto per circa sei anni. – Marcin

+0

Dovresti mostrare altro codice. Come si può dirti come 'myobj.close()' cambia le cose, senza sapere cosa fa? – ugoren

+1

Ricorda che 'del' non chiama' __del__'. Rimuove solo uno dei riferimenti. In genere è meglio chiudere esplicitamente, possibilmente usando il protocollo di gestione del contesto (istruzione 'with'). – yak

risposta

8

Sei sicuro di voler utilizzare __del__? Ci sono issues con __del__ e garbage collection.

Si potrebbe fare MyClass un context manager invece:

class MyClass(object): 
    def __enter__(self): 
     return self 
    def __exit__(self,ext_type,exc_value,traceback): 
     if self.__fileHandle__ is not None: 
       self.__fileHandle__.close() 

In questo modo, si potrebbe usare MyClass come questo:

with MyClass() as myobj: 
    ... 

e myobj.__exit__ (e quindi self.__fileHandle__.close()) verrà chiamato quando Python foglie il with-block.

+0

+1 per aver menzionato i problemi. http://docs.python.org/library/gc.html#gc.garbage dice che «Gli oggetti che hanno __del __() metodi e fanno parte di un ciclo di riferimento fanno sì che l'intero ciclo di riferimento non sia riscontrabile, inclusi oggetti non necessariamente in il ciclo ma raggiungibile solo da esso. » –

+0

Non vedo come ci sia un ciclo di riferimento nell'esempio dell'OP. C'è un oggetto, ottiene 'del''d, e' __del __() 'non viene chiamato. – sudo

+1

Inoltre, nella mia situazione, preferirei lasciare che il GC gestisca le cose in quanto non ha bisogno di chiudersi immediatamente, solo alla fine ... ma ha sempre problemi. E no, i gestori di contesto non sono una soluzione reale. Vi impediscono di chiudere l'oggetto all'interno della stessa chiamata di funzione e di indentare il codice un livello ogni volta che ne usate uno. Rende illeggibile quando voglio creare 20 di questi. – sudo

8

Questo non è quello che fa del. È spiacevole che lo __del__ abbia lo stesso nome di del, perché non sono collegati tra loro. Nella terminologia moderna, il metodo __del__ si chiamerebbe un finalizzatore , non un distruttore e la differenza è importante.

La breve differenza è che è facile da garantire quando un distruttore si chiama, ma avete molto poche garanzie su quando sarà chiamato __del__ e potrebbe mai essere chiamato. Ci sono molte diverse circostanze che possono causare questo.

Se si desidera lo scoping lessicale, utilizzare un'istruzione with. Altrimenti, chiama direttamente il myobj.close(). L'istruzione del elimina solo i riferimenti, non gli oggetti.

Ho trovato un'altra risposta (link) a un'altra domanda che risponde in modo più dettagliato. È un peccato che la risposta accettata a questa domanda contenga errori eclatanti.

Modifica: Come osservato dai commentatori, è necessario ereditare da object. Va bene, ma è comunque possibile che __del__ non venga mai chiamato (potresti essere fortunato). Vedi la risposta collegata sopra.

2

Il tuo codice dovrebbe ereditare da object - non farlo è stato considerato scaduto (tranne in casi speciali) per almeno sei anni.

potete leggere qui __del__: http://docs.python.org/reference/datamodel.html#object.del

versione corta del perché è necessario ereditare da object è che __del__ è solo "magia", a classi di nuovo stile.

Se è necessario fare affidamento sulla chiamata di un finalizzatore, suggerisco caldamente di utilizzare l'approccio del gestore di contesto consigliato in altre risposte, poiché si tratta di una soluzione portatile e affidabile.

+1

Ah ho appena trovato un'istanza in cui questa "magia" non funziona ... usando invece ipython. – tdc

+0

@tdc: A meno che non mi sbagli, ipython è basato su cpython. In ogni caso, se ti affidi al comportamento di una particolare istanza, il tuo codice sarà non portatile. Probabilmente sarebbe meglio usare l'approccio del gestore di contesto raccomandato in altre risposte. – Marcin

0

Forse qualcos'altro sta facendo riferimento a esso, motivo per cui __del__ non viene chiamato (ancora).

Considerate questo codice:

#!/usr/bin/env python 

import time 

class NiceClass(): 
    def __init__(self, num): 
     print "Make %i" % num 
     self.num = num 
    def __del__(self): 
     print "Unmake %i" % self.num 

x = NiceClass(1) 
y = NiceClass(2) 
z = NiceClass(3) 
lst = [x, y, z] 
time.sleep(1) 
del x 
del y 
del z 
time.sleep(1) 
print "Deleting the list." 
del lst 
time.sleep(1) 

Non chiama __del__ di NiceClass casi fino a quando cancelliamo la lista che li fa riferimento.

A differenza di C++, __del__ non viene chiamato incondizionatamente per distruggere l'oggetto su richiesta. GC rende le cose un po 'più difficili. Ecco alcune informazioni: http://arctrix.com/nas/python/gc/

+0

In questo caso c'era solo un riferimento – tdc

Problemi correlati