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 Kalem

             

execfile (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

È stato utile?

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__.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top