2014-04-04 9 views
51

Questo codice ...Una semplice differenza nei nomi delle variabili Python3 altera il modo in cui il codice viene eseguito?

class Person: 
    num_of_people = 0 

    def __init__(self, name): 
     self.name = name 
     Person.num_of_people += 1 

    def __del__(self): 
     Person.num_of_people -= 1 

    def __str__(self): 
     return 'Hello, my name is ' + self.name 

cb = Person('Corey') 
kb = Person('Katie') 
v = Person('Val') 

produce il seguente errore ...

Exception AttributeError: "'NoneType' object has no attribute 'num_of_people'" in <bound method Person.__del__ of <__main__.Person object at 0x7f5593632590>> ignored 

Ma questo codice non lo fa.

class Person: 
    num_of_people = 0 

    def __init__(self, name): 
     self.name = name 
     Person.num_of_people += 1 

    def __del__(self): 
     Person.num_of_people -= 1 

    def __str__(self): 
     return 'Hello, my name is ' + self.name 

cb = Person('Corey') 
kb = Person('Katie') 
vb = Person('Val') 

L'unica differenza che vedo è che l'ultimo nome di variabile è "vb" vs. "v".

Sto appoggiando Python e sto lavorando alle cose OOP ora.

+2

@StevenRumbalski: In breve, sì. Ma solo all'uscita dell'interprete. –

+6

Il primo codice non produce quell'eccezione. Mostra il tuo traceback completo. (Correzione: non produce quell'eccezione in Python 3.3 o versioni successive. In 3.2 lo fa.) – geoffspear

+0

@Wooble Nah! Questo è quello che mi mancava .. – aIKid

risposta

59

Sì, anche se non è tanto il nome della variabile che causa questo, non direttamente.

Quando Python si chiude, anche tutti i moduli vengono cancellati. Il modo in cui i moduli vengono ripuliti consiste nell'impostare tutti i globali in un modulo su None (quindi tali riferimenti non si riferiscono più agli oggetti originali). Quelle globulari sono le chiavi in ​​un oggetto dizionario e poiché i dizionari sono ordinati in modo arbitrario, rinominare una variabile può cambiare l'ordine in cui le variabili vengono cancellate.

Dopo aver rinominato v in vb, è stato modificato l'ordine in cui le variabili vengono cancellate e ora Person è stato cancellato per ultimo.

Un work-around è quello di utilizzare type(self).num_of_people -= 1 nel metodo __del__ invece:

def __del__(self): 
    type(self).num_of_people -= 1 

perché l'istanza avrà sempre un riferimento alla classe ancora, o prova se Person non è impostato None:

def __del__(self): 
    if Person is not None: 
     Person.num_of_people -= 1 

Due note:

  • CPython 3.4 non imposta più globali su None (nella maggior parte dei casi), come da Safe Object Finalization; vedi PEP 442.

  • CPython 3.3 applica automaticamente un randomized hash salt alle chiavi str utilizzate in un dizionario globals; questo rende il comportamento che hai osservato ancora più casuale, ma semplicemente rieseguire il tuo codice più volte potrebbe o non potrebbe far scattare il messaggio di errore.

+2

Ugh .. La risposta è brillante, ma non lo capisco davvero. Cosa intendi con "in intepreter exit"? Puoi spiegare per favore? – aIKid

+3

@aikid: quando Python termina, (o se si cancella il modulo rimuovendo tutti i riferimenti e cancellandolo da 'sys.modules', ma ciò non accade spesso), viene chiamato il modulo' __del__', che quindi procede per per prima cosa cancelli tutti i globals nel modulo ribattendo i loro nomi a 'None'. –

+0

Quindi l'eccezione viene sollevata solo quando usciamo dall'interprete .. <- Questo è falso, giusto? – aIKid

Problemi correlati