2009-12-20 8 views
6

Sono nuovo in Python e ho lavorato con gli esempi in "A Byte of Python" di Swaroop CH. Sto vedendo alcuni comportamenti con il metodo __del__ che mi sta sconcertando.Il metodo __del__ viene chiamato in python quando non è previsto

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 ')
Inizializzazione Swaroop
inizializzazione Kalem

execfile (u'C: \ 1_eric \ Python \ test1.py ')
Inizializzazione Swaroop
Swaroop dice bye
Io sono l'ultimo
inizializzazione Kalem
Kalem dice bye
Io sono il ultimo

Perché il __del__ metodo viene chiamato immediatamente dopo esimo e __init__ alla seconda esecuzione?
Sto indovinando che dal momento che vengono utilizzati gli stessi nomi di istanza ("swaroop" e "kaleem") che sta rilasciando l'istanza originale e la raccolta dei dati inutili. Ma sembra che ciò stia sconvolgendo l'attuale numero di abitanti.

Cosa sta succedendo qui?
Qual è un buon modo per evitare questo tipo di confusione?
Evitare l'uso di __del__? Verificare la presenza di nomi di istanza esistenti prima di riutilizzarli? ...

Grazie, Eric

+2

Non si dovrebbe fare affidamento sul metodo '__del__' essere chiamato in un determinato momento in ogni caso. In futuro il garbage collector potrebbe cambiare in modo che '__del__' venga chiamato in momenti diversi. –

+0

@Soviut (che lo ha ritradotto in modo errato): questo non ha nulla a che fare con "classmethods" in Python. Usa un attributo di classe, ma non è la stessa cosa. –

+0

Benvenuti in Stack Overflow! Quando hai ricevuto una buona risposta alla tua domanda, per favore "accetta" la risposta più utile facendo clic sul riquadro della casella di controllo a sinistra della risposta. Questo indica che hai ricevuto una risposta che ha funzionato per te. Ciò consente di aumentare la tua reputazione sul sito. –

risposta

7

Consiglio generale: non usare __ del __ in Python. Può rompere la raccolta dei rifiuti in molti modi, esp. nel caso di riferimenti circolari tra oggetti.

Nel tuo esempio, ci sono vari problemi relativi all'uso di execfile() - che non è una buona pratica - e la ridefinizione delle variabili globali. A proposito, se hai davvero bisogno di creare uno pseudo-distruttore (cioè un codice che viene invocato ogni volta che l'oggetto viene sottoposto a garbage collection), scrivi una cosiddetta funzione "finalizzatore" (non è propriamente un distruttore) e invocala usando weakref .ref callback. NON dovrebbe essere un metodo di istanza, naturalmente, e ricorda che lambda crea effettivamente una chiusura, quindi assicurati di non far trapelare alcun riferimento a te nella callback! Se hai bisogno di dati dall'istanza distrutta, usa l'approccio func default, ma assicurati che non sia mai per fare riferimento a 'self' 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 
+0

Grazie a Greg e Alan per le spiegazioni, ora capisco cosa sta succedendo e che usare __del__ ha problemi. Una cosa che mi dà ancora fastidio è che se mi "dimentico" di tutti i globals nello script precedente ed eseguo lo script due volte di fila (all'interno dell'IDE Spyder, che usa execfile()) vedo ancora il finalizzatore chiamato subito dopo l'inizializzatore, Inizializzazione di uno uno dice ciao Io sono l'ultimo cioè la pulizia del "vecchio" p1 avviene dopo p1 viene riassegnato. Immagino di dover stare attento quando si riassegnano variabili "globali" (o si usano variabili temporanee come "tempVar". – ELee

+2

Meglio, dovresti ricordare che Python non ha il concetto di "variabili" nel senso che le lingue sono tipizzate staticamente. solo oggetti e nomi che legano tali oggetti - una volta che un oggetto non è referenziato da nulla, sarà distrutto - ma non c'è "posizione di memoria" il cui valore sarà sovrascritto quando cambi il valore - l'operatore "=" __rebinds__ il nome a un riferimento diverso invece. –

19

Ci sono un paio di cose che sta succedendo qui. Quando la tua classe Person4 viene istanziata, inizializza la sua variabile di classe population su 0. Dalla tua console interattiva, sembra che tu esegua il file "test1.py" più volte. La seconda volta che lo esegui, la classe Person4 viene dichiarata di nuovo che lo rende tecnicamente diverso dal dal primo (anche se ha lo stesso nome). Ciò significa che ha il proprio conteggio indipendente population.

Ora, swaroop e kaleem sono globali variabili, condivisi tra le due istanze di "test1.py". Python utilizza internamente il conteggio dei riferimenti per la maggior parte della sua garbage collection automatica, pertanto l'istanza originale della prima classe Person4 non viene rilasciata fino al secondo assegnamento a swaroop. L'assegnazione a swaroop riduce il conteggio dei riferimenti per la prima istanza, causando la chiamata di __del__ perché il conteggio dei riferimenti è ora zero.Ma perché ti riferisci Person4 per nome all'interno __del__(), quando il precedente esempio scompare decrementa il conteggio nuovaPerson4.population, al posto del vecchio conte Person4 popolazione.

Speriamo che abbia senso. Posso capire perché questo potrebbe essere fonte di confusione per qualcuno che sta imparando Python. L'utilizzo di variabili di classe contemporaneamente alla ridefinizione della classe Person4 utilizzando execfile() confonde ulteriormente la questione. Per quello che vale, ho scritto un sacco di codice Python e non penso di aver mai avuto bisogno di usare il metodo speciale __del__.

+1

+1 per una buona scrittura – ChristopheD

Problemi correlati