Metodo __del__ chiamato in python, quando non è previsto
-
20-09-2019 - |
Domanda
Sono nuovo di pitone e hanno lavorato attraverso gli esempi in Swaroop CH di "A Byte of Python". Sto vedendo alcuni comportamenti con il metodo __del__
che mi è sconcertante.
In sostanza, se si esegue il seguente script (in Python 2.6.2)
class Person4:
'''Represents a person'''
population = 0
def __init__(self, name):
'''Initialize the person's data'''
self.name = name
print 'Initializing %s'% self.name
#When the person is created they increase the population
Person4.population += 1
def __del__(self):
'''I am dying'''
print '%s says bye' % self.name
Person4.population -= 1
if Person4.population == 0:
print 'I am the last one'
else:
print 'There are still %d left' % Person4.population
swaroop = Person4('Swaroop')
kaleem = Person4('Kalem')
utilizzando la console di Python (o la console interattiva Spyder) Vedo il seguente:
execfile (u'C: \ 1_eric \ Python \ test1.py ')
L'inizializzazione Swaroop
Inizializzazione Kalemexecfile (u'C: \ 1_eric \ Python \ test1.py ')
L'inizializzazione Swaroop
Swaroop dice bye
Io sono l'ultimo
L'inizializzazione Kalem
Kalem dice bye
Io sono l'ultimo
Perché è il metodo __del__
essere chiamato subito dopo la __init__
sul secondo periodo?
Sto indovinando che, poiché gli stessi nomi di istanza ( 'Swaroop' e 'Kaleem') vengono utilizzati che sta rilasciando l'istanza originale e la spazzatura raccolta esso. Ma, questo sembra giocare il caos con il conteggio attuale popolazione.
Che cosa sta succedendo qui?
Che cosa è un buon modo per evitare questo tipo di confusione?
Evitare l'uso di __del__
?
Verificare la presenza di nomi di istanza esistente prima di riutilizzarli?
...
Grazie, Eric
Soluzione
Informazione generale: non utilizzare __ del __ in Python. Si può rompere la raccolta dei rifiuti in diversi modi, esp. nel caso di riferimenti circolari tra oggetti.
Nel tuo esempio, ci sei diverse problematiche legate all'utilizzo di execfile () - che non è una buona pratica - e la ridefinizione delle variabili globali. A proposito, se si ha realmente bisogno per creare una pseudo-distruttore (cioè un codice che viene richiamato ogni volta che l'oggetto viene garbage collection), scrivere la cosiddetta funzione "finalizzatore" (che non è propriamente un distruttore) e invocare usando weakref callback .REF. Non dovrebbe essere un metodo, naturalmente esempio, e ricordate che lambda crea effettivamente una chiusura, e quindi essere sicuri di non perdere alcun riferimento a sé nel callback! Se avete bisogno di dati dall'istanza distrutta, utilizzare l'approccio argomento func di default, tanto per essere sicuro non per fare riferimento a 'sé' all'interno del lambda, altrimenti non funzionerà.
from weakref import ref
from time import sleep
class Person4:
'''Represents a person'''
population = 0
def __init__(self, name):
'''Initialize the person's data'''
self.name = name
print 'Initializing %s'% self.name
#When the person is created they increase the population
Person4.population += 1
self._wr = ref(self, lambda wr, name=self.name: Person4_finalizer(name))
def Person4_finalizer(name):
'''I am dying'''
print '%s says bye' % name
Person4.population -= 1
if Person4.population == 0:
print 'I am the last one'
else:
print 'There are still %d left' % Person4.population
p1 = Person4("one")
p2 = Person4("two")
p3 = Person4("three")
del p2
del p3
sleep(5)
uscita (il sonno è lì per aiutare a vedere cosa sta succedendo):
Initializing one
Initializing two
Initializing three
two says bye
There are still 2 left
three says bye
There are still 1 left
one says bye
I am the last one
Altri suggerimenti
Ci sono un paio di cose che sta succedendo qui. Quando la classe Person4
viene creata un'istanza, si inizializza la sua variabile di classe population
a 0. Dalla console interattiva, in cui sembra essere in esecuzione i tuoi più volte file "test1.py". La seconda volta che lo si esegue, la classe Person4
è dichiarato ancora una volta che lo rende tecnicamente diverso dalla prima (anche se ha lo stesso nome). Ciò significa che ha un proprio numero di population
indipendente.
Ora, swaroop
e kaleem
sono globale variabili, condiviso tra le due istanze di "test1.py". Pitone utilizza internamente conteggio di riferimento per la maggior parte della sua garbage collection automatica, quindi l'istanza originale della prima classe Person4
non viene rilasciata fino alla seconda assegnazione a swaroop
. Assegnando swaroop
decrementa il conteggio di riferimento per la prima istanza, causando __del__
essere chiamato perché il conteggio di riferimento è ora zero. Ma perché ti riferisci Person4
per nome all'interno __del__()
, quando il precedente istanza scompare decrementa la nuovo Person4.population
conteggio, al posto del vecchio conte popolazione Person4
.
Si spera che avesse senso. Posso capire perché questo potrebbe essere fonte di confusione a qualcuno l'apprendimento Python. L'utilizzo di variabili di classe allo stesso tempo a ridefinire la classe Person4
utilizzando execfile()
è ulteriormente questioni confuse. Per quel che vale, ho scritto un sacco di codice Python e non credo che abbia mai avuto bisogno di usare il metodo speciale __del__
.