2015-02-03 10 views
5

A prima vista, sembra che il metodo speciale Python __del__ offra gli stessi vantaggi di un distruttore in C++. Ma secondo la documentazione di Python (https://docs.python.org/3.4/reference/datamodel.html), c'è non c'è nessuna garanzia che il metodo __del__ del tuo oggetto venga mai chiamato!RAII in Python: qual è il punto di __del__?

Non è garantito che i metodi __del__() vengano chiamati per oggetti che esistono ancora quando l'interprete esce.

Quindi, in altre parole, il metodo è inutile! Non è vero? Una funzione di hook che può o non può essere chiamata in realtà non fa molto bene, quindi __del__ non offre nulla per quanto riguarda RAII. Se ho un po 'di pulizia essenziale, non ho bisogno di eseguire alcune volte, oh, quando mai il GC sembra davvero, ho bisogno di eseguire in modo affidabile, deterministico e al 100% del tempo.

So che Python fornisce i gestori di contesto, che sono molto più utili per tale compito, ma perché è stato mantenuto il __del__? Qual e il punto?

+0

Non capisco davvero cosa stai chiedendo esattamente. La tua domanda "È' __del__' essenzialmente inutile? "? (Credo che sia la tua domanda) In questo caso credo che dovresti * chiarire * la questione rimuovendo tutti i riferimenti a C++ o RAII, che sono completamente estranei. Se la tua domanda è "Come implementare RAII in Python? Posso usare" __del__ "per questo?" allora probabilmente devi cambiare un sacco di cose nella tua domanda. – Bakuriu

+0

@Bakuriu Il nocciolo della questione è, qual è esattamente il punto di "_del__"? Qualcuno può nominare un caso d'uso realistico per il metodo? – antred

risposta

1

Perché __del__ viene chiamato. È solo che non è chiaro quando lo farà, perché in CPython se si dispone di riferimenti circolari, il meccanismo refcount non può occuparsi del recupero dell'oggetto (e quindi della sua finalizzazione tramite __del__) e delegarlo al garbage collector.

Il garbage collector ha quindi un problema: non può sapere in che ordine interrompere i riferimenti circolari, poiché ciò potrebbe causare ulteriori problemi (ad esempio libera la memoria che sarà necessaria nella finalizzazione di un altro oggetto che è parte del loop raccolto, attivando un segfault).

Il punto da sottolineare è che l'interprete può uscire per ragioni che impediscono di eseguire la pulizia (ad esempio, segfaults, o qualche modulo C chiama in modo imprevisto exit()).

C'è PEP 442 per la finalizzazione di oggetti sicuri che è stata finalizzata in 3.4. Ti suggerisco di dare un'occhiata a questo.

https://www.python.org/dev/peps/pep-0442/

+0

Che di nuovo chiarisce quanto sia inutile '__del__'. Per esempio, se io dovessi usare gli oggetti di una classe mutex guard, non potrei fare lo sblocco nel metodo '__del__' della classe guard, perché non è abbastanza sapere che' __del__' verrà chiamato quando mai il GC si sente come facendo il suo lavoro ... Ho bisogno del mutex rilasciato proprio quando l'oggetto guard "esce dal campo" (lo so che lo sta allungando, date le regole di scoping di Python). IMO '__del__' dà ai programmatori la falsa impressione che RAII in Python sia fattibile tramite distruttori quando in realtà non lo è. Devi usare i gestori di contesto per quello. – antred

+1

Non è inutile. Stai provando a codificare C++ in python. Il punto è che '__del__' non è un distruttore. È un meccanismo che è in atto e può essere utile in casi molto limitati, ma non si può fare affidamento su di esso, simile al java finalizer. Vedi questo thread per una domanda simile http://stackoverflow.com/questions/158174/why-would-you-ever-implement-finalize –

9

__del__ è un finalizzatore. Non è un distruttore. Finalizzatori e distruttori sono animali completamente diversi.

I distruttori sono chiamati in modo affidabile e esistono solo nelle lingue con gestione della memoria deterministica (come C++). I gestori di contesto Python (la dichiarazione with) possono ottenere effetti simili in determinate circostanze. Questi sono affidabili perché la durata di vita di un oggetto è fissata con precisione; in C++, gli oggetti muoiono quando sono esplicitamente delete d o quando viene chiuso un certo scope (o quando un puntatore intelligente li cancella in risposta alla propria distruzione). E quello è il momento in cui i distruttori corrono.

I finalizzatori non vengono richiamati in modo affidabile. L'unico uso valido di un finalizzatore è come an emergency safety net (NB: questo articolo è scritto da una prospettiva .NET, ma i concetti si traducono abbastanza bene). Ad esempio, gli oggetti file restituiti da open() si chiudono automaticamente quando finalizzati. Ma devi comunque chiuderli tu stesso (ad esempio usando la dichiarazione with). Questo perché gli oggetti vengono distrutti dinamicamente dal garbage collector, che può essere o non essere eseguito immediatamente e, con la garbage collection generazionale, può o meno raccogliere alcuni oggetti in un dato passaggio.Dato che nessuno sa quali tipi di ottimizzazioni potremmo inventare in futuro, è più sicuro presumere che non puoi sapere quando il garbage collector andrà in giro a raccogliere i tuoi oggetti. Ciò significa che non puoi fare affidamento sui finalizzatori.

Nel caso specifico di CPython, si ottengono garanzie leggermente più forti, grazie all'uso del conteggio dei riferimenti (che è molto più semplice e prevedibile della garbage collection). Se è possibile assicurarsi di non creare mai un ciclo di riferimento che coinvolga un determinato oggetto, il finalizzatore dell'oggetto verrà chiamato in un punto prevedibile (quando l'ultimo riferimento muore). Questo è solo true di CPython, l'implementazione di riferimento e non di PyPy, IronPython, Jython o altre implementazioni.

+1

Una delle virgolette nella pagina a cui è collegato è > Un programma scritto correttamente non posso presumere che i finalizzatori funzioneranno mai. Questo semplicemente rafforza la mia convinzione che siano essenzialmente inutili. Anche il tuo commento sul fatto che possano essere usati come una rete di sicurezza di emergenza per cercare di ripulire roba che il programmatore ha dimenticato non cambia in realtà perché, ancora, potrebbe accadere o meno. Ad ogni modo, penso che questa sia un'idea orribile. Crea solo un falso senso di sicurezza, crea confusione e incoraggia la gestione scarsamente delle risorse (hey, non devo ripulire, Python __might__ lo faccio solo per me). – antred

+1

Sono d'accordo con te. Ecco perché non uso mai i finalizzatori. OTOH, i finalizzatori associati a riferimenti deboli possono a volte essere utili, se per esempio è necessario mantenere una collezione di oggetti live ma non mantenerli vivi. Vedi 'weakref' per maggiori informazioni. – Kevin

+0

PyPy garantisce che il tuo __del__ verrà chiamato comunque. In Cpython se fa parte del ciclo, non verrà chiamato AT ALL. – fijal