__del__(self)
non riesce con un'eccezione AttributeError. Capisco Python doesn't guarantee l'esistenza di "variabili globali" (dati dei membri in questo contesto?) Quando viene invocato il numero __del__()
. Se questo è il caso e questo è il motivo dell'eccezione, come posso assicurarmi che l'oggetto si distrugga correttamente?Come ripulire correttamente un oggetto Python?
risposta
Si consiglia di utilizzare la dichiarazione with
di Python per la gestione delle risorse che devono essere ripulite. Il problema con l'utilizzo di un'esplicita dichiarazione close()
è che devi preoccuparti che le persone si dimentichino di chiamarlo o dimenticare di inserirlo in un blocco finally
per impedire una perdita di risorse quando si verifica un'eccezione.
utilizzare l'istruzione with
, creare una classe con i seguenti metodi:
def __enter__(self)
def __exit__(self, exc_type, exc_value, traceback)
Nel tuo esempio sopra, utilizza
class Package:
def __init__(self):
self.files = []
def __enter__(self):
return self
# ...
def __exit__(self, exc_type, exc_value, traceback):
for file in self.files:
os.unlink(file)
Poi, quando qualcuno ha voluto usare la tua classe , farebbero quanto segue:
with Package() as package_obj:
# use package_obj
La variabile package_obj sarà un istanza di tipo Pacchetto (è il valore restituito dal metodo __enter__
). Il suo metodo __exit__
verrà chiamato automaticamente, indipendentemente dal fatto che si verifichi o meno un'eccezione.
Si potrebbe anche prendere questo approccio un ulteriore passo avanti. Nell'esempio sopra, qualcuno potrebbe ancora istanziare Package usando il suo costruttore senza usare la clausola with
. Non vuoi che ciò accada. È possibile risolvere questo problema creando una classe PackageResource che definisce i metodi __enter__
e __exit__
. Quindi, la classe Package verrà definita rigorosamente all'interno del metodo __enter__
e restituita.In questo modo, il chiamante non potrebbe creare un'istanza della classe pacchetto senza utilizzare un with
dichiarazione:
class PackageResource:
def __enter__(self):
class Package:
...
self.package_obj = Package()
return self.package_obj
def __exit__(self, exc_type, exc_value, traceback):
self.package_obj.cleanup()
devi usare questo come segue:
with PackageResource() as package_obj:
# use package_obj
Tecnicamente parlando, si potrebbe chiamare PackageResource() .__ inserire __() in modo esplicito e quindi creare un pacchetto che non sarebbe mai finalizzato ... ma dovevano davvero cercare di infrangere il codice. Probabilmente non è qualcosa di cui preoccuparsi. –
A proposito, se stai usando Python 2.5, dovrai fare da __future__ import with_statement per poter usare l'istruzione with. –
Ho trovato un articolo che aiuta a mostrare perché __del __() agisce come fa e dà credito all'utilizzo di una soluzione di gestione del contesto: http://www.andy-pearce.com/blog/posts/2013/Apr/python- distruttore-svantaggi/ – eikonomega
Basta avvolgere il distruttore con un'istruzione try/except e non genererà un'eccezione se i globals sono già eliminati.
Modifica
Prova questa:
from weakref import proxy
class MyList(list): pass
class Package:
def __init__(self):
self.__del__.im_func.files = MyList([1,2,3,4])
self.files = proxy(self.__del__.im_func.files)
def __del__(self):
print self.__del__.im_func.files
Sarà roba l'elenco dei file nella del funzionamento che è garantito per esistere al momento della chiamata. Il proxy weakref è di impedire a Python, o se stessi, di eliminare in qualche modo la variabile self.files (se viene cancellata, quindi non avrà effetto sull'elenco dei file originale). Se non è il caso in cui questo viene eliminato anche se ci sono più riferimenti alla variabile, è possibile rimuovere l'incapsulamento del proxy.
Il problema è che se i dati dei membri sono spariti è troppo tardi per me. Ho bisogno di quei dati. Vedi il mio codice qui sopra: ho bisogno dei nomi dei file per sapere quali file rimuovere. Ho semplificato il mio codice, ma ci sono altri dati che ho bisogno di ripulire da solo (cioè l'interprete non saprà come pulire). – wilhelmtell
Questa soluzione ha funzionato molto bene nel mio caso. – gaborous
Sembra che il modo idiomatico per fare ciò sia fornire un metodo close()
(o simile) e chiamarlo esplicitamente.
Questo è l'approccio che ho usato prima, ma mi sono imbattuto in altri problemi. Con le eccezioni lanciate dappertutto da altre librerie, ho bisogno dell'aiuto di Python per ripulire il casino in caso di errore. In particolare, ho bisogno che Python chiami il distruttore per me, perché altrimenti il codice diventa rapidamente ingestibile, e sicuramente dimenticherò un punto di uscita dove dovrebbe essere una chiamata a .close(). – wilhelmtell
Non credo che sia possibile per i membri di istanza da rimuovere prima che venga chiamato __del__
. La mia ipotesi sarebbe che la ragione del tuo particolare AttributeError è da qualche altra parte (forse rimuovi per errore self.file altrove).
Tuttavia, come hanno sottolineato gli altri, evitare di utilizzare __del__
. La ragione principale di ciò è che le istanze con __del__
non verranno raccolte automaticamente (verranno liberate solo quando il loro account raggiunge 0). Pertanto, se le tue istanze sono coinvolte in riferimenti circolari, vivranno in memoria fino a quando l'applicazione verrà eseguita. (Potrei sbagliarmi su tutto questo però, dovrei leggere di nuovo i documenti gc, ma sono piuttosto sicuro che funzioni così).
Gli oggetti con '__del__' possono essere garbage collection se il loro conteggio di riferimento da altri oggetti con' __del__' è zero e sono irraggiungibili. Questo significa che se hai un ciclo di riferimento tra oggetti con '__del__', nessuno di questi verrà raccolto. Qualsiasi altro caso, tuttavia, dovrebbe essere risolto come previsto. – Collin
Penso che il problema potrebbe essere in __init__
se c'è più codice di quello mostrato?
__del__
verrà chiamato anche se __init__
non è stato eseguito correttamente o ha generato un'eccezione.
I suoni sono molto probabili. Il modo migliore per evitare questo problema quando si usa '__del__' è dichiarare esplicitamente tutti i membri a livello di classe, assicurandosi che esistano sempre, anche se' __init__' fallisce. Nell'esempio fornito, 'files =()' funzionerebbe, anche se principalmente assegneresti 'None'; in entrambi i casi, è comunque necessario assegnare il valore reale in '__init__'. –
Come appendice a Clint's answer, è possibile semplificare PackageResource
utilizzando contextlib.contextmanager
:
@contextlib.contextmanager
def packageResource():
class Package:
...
package = Package()
yield package
package.cleanup()
In alternativa, anche se probabilmente non come Pythonic, è possibile ignorare Package.__new__
:
class Package(object):
def __new__(cls, *args, **kwargs):
@contextlib.contextmanager
def packageResource():
# adapt arguments if superclass takes some!
package = super(Package, cls).__new__(cls)
package.__init__(*args, **kwargs)
yield package
package.cleanup()
def __init__(self, *args, **kwargs):
...
e utilizzare semplicemente with Package(...) as package
.
per ottenere le cose più breve, denominare la funzione di pulizia close
e utilizzare contextlib.closing
, nel qual caso è possibile utilizzare la non modificato Package
classe tramite with contextlib.closing(Package(...))
o ignorare la sua __new__
al semplice
class Package(object):
def __new__(cls, *args, **kwargs):
package = super(Package, cls).__new__(cls)
package.__init__(*args, **kwargs)
return contextlib.closing(package)
E questo costruttore viene ereditato , quindi puoi semplicemente ereditare, ad es
class SubPackage(Package):
def close(self):
pass
** Questo è fantastico. ** Mi piace particolarmente l'ultimo esempio. È sfortunato che non possiamo evitare la combinazione di quattro righe del metodo 'Package .__ new __()', tuttavia. O forse possiamo. Potremmo probabilmente definire un decoratore di classe o una metaclasse che generano per noi questo standard. Cibo per il pensiero pitonico. –
@CecilCurry Grazie, e buon punto. Anche qualsiasi classe che eredita da 'Package' dovrebbe farlo (sebbene non l'abbia ancora testata), quindi non dovrebbe essere richiesto alcun metaclass. Anche se ho trovato alcuni modi piuttosto curiosi di utilizzare le metaclassi in passato ... –
@CecilCurry In realtà, il costruttore è ereditato, quindi puoi usare 'Pacchetto' (o meglio una classe chiamata' Closing') come genitore di classe invece di 'object'. Ma non chiedermi in che modo l'ereditarietà multipla incasini questo ... –
Il metodo standard è quello di utilizzare atexit.register
:
# package.py
import atexit
import os
class Package:
def __init__(self):
self.files = []
atexit.register(self.cleanup)
def cleanup(self):
print("Running cleanup...")
for file in self.files:
print("Unlinking file: {}".format(file))
# os.unlink(file)
Ma si dovrebbe tenere a mente che questo persisterà tutte le istanze create su Package
fino Python è terminato.
Demo utilizzando il codice di cui sopra salvato package.py:
$ python
>>> from package import *
>>> p = Package()
>>> q = Package()
>>> q.files = ['a', 'b', 'c']
>>> quit()
Running cleanup...
Unlinking file: a
Unlinking file: b
Unlinking file: c
Running cleanup...
Una migliore alternativa è quella di utilizzare weakref.finalize. Vedere gli esempi allo Finalizer Objects e allo Comparing finalizers with __del__() methods.
Usato così oggi e funziona perfettamente, meglio di altre soluzioni. Ho una classe di comunicatore basata su multiprocessing che apre una porta seriale e quindi ho un metodo 'stop()' per chiudere le porte e 'join()' i processi. Tuttavia, se i programmi si chiudono inaspettatamente "stop()" non viene chiamato - l'ho risolto con un finalizzatore. Ma in ogni caso chiamo '_finalizer.detach()' nel metodo stop per evitare di chiamarlo due volte (manualmente e successivamente dal finalizzatore). –
- 1. Come ripulire il contesto di un oggetto Entity Framework?
- 2. Come posso "ripulire" un virtualenv?
- 3. Come ripulire le filettature
- 4. Come rendere un oggetto correttamente lavabile?
- 5. Come ripulire "un tipo è stato dedotto come avviso" Qualsiasi "?
- 6. Come ripulire Eclipse "esegui configurazioni"
- 7. Capistrano - ripulire vecchi rilasci
- 8. Zend: come distruggere correttamente un oggetto personalizzato in PHP 7?
- 9. Qual è il modo migliore per ripulire un oggetto in Java?
- 10. Come clonare un oggetto generatore Python?
- 11. Come ripulire vecchie build fallite in TeamCity?
- 12. PHP Injection Attack - come ripulire il casino?
- 13. Come ripulire gli eventi assegnati dal controller?
- 14. JBoss AS 7: Come ripulire il tmp?
- 15. Python Parse CSV Correttamente
- 16. Come decorare un oggetto Python con un mutex
- 17. Il modo migliore per 'ripulire' testo HTML
- 18. Python come sapere se un record inserito correttamente o no
- 19. Come dichiarare correttamente un ctype Structure + Union in Python?
- 20. Come utilizzare correttamente coverage.py in Python?
- 21. Python e dizionario come oggetto
- 22. Come posso parlare correttamente in Python?
- 23. Come iterare correttamente con re.sub() in Python
- 24. come scegliere un solo oggetto da un generatore (in python)?
- 25. Come estendere correttamente altre classi in Python? (python v3.3)
- 26. Come gestire correttamente le connessioni Redis con Python/Python RQ?
- 27. Come posso copiare un oggetto immutabile come tupla in Python?
- 28. correttamente allineati o sovrapposte da un errore campo non oggetto
- 29. Ruota correttamente un oggetto 3D su 3 assi in JavaFX
- 30. C'è un modo per ripulire l'html prodotto da jinja2?
Leggere ciò che hai collegato, le variabili globali che vanno via non sembra applicarsi qui a meno che tu non stia parlando di quando stai programmando di uscire, durante il quale suppongo che in base a ciò che hai collegato potrebbe essere POSSIBILE che il modulo os stesso sia già andato. Altrimenti, non penso che si applichi alle variabili membro in un metodo __del __(). –
L'eccezione viene lanciata molto prima che il mio programma venga chiuso. L'eccezione AttributeError che ottengo è che Python dice che non riconosce self.files come un attributo di Package. Potrei sbagliarmi, ma se per "globals" non significano variabili globali ai metodi (ma possibilmente locali alla classe), allora non so cosa causi questa eccezione. Indizi di Google Python si riserva il diritto di pulire i dati dei membri prima che venga chiamato __del __ (self). – wilhelmtell
Il codice pubblicato sembra funzionare per me (con Python 2.5). Puoi pubblicare il codice effettivo che sta fallendo o semplificato (più semplice è la versione che causa ancora l'errore? – Silverfish