2015-12-15 26 views
9

Qualcuno potrebbe spiegarmi il seguente codice.Come creare istanze distinte di una classe in Python?

class InnerTest: 

    def __init__(self, value = 0): 
     self.value = value 

class OuterTest: 

    def __init__(self, inner_test = InnerTest()): 
     self.inner_test = inner_test 

a = OuterTest() 
b = OuterTest() 

a.inner_test.value = 42 
print b.inner_test.value 

Esso stampa 42, mi aspettavo 0.

Volevo creare due istanze di OuterTest, che conterrebbe un'istanza distinta di InnerTest ciascuna. Invece ho ottenuto due istanze di OuterTest che fanno riferimento alla stessa istanza di InnerTest.

Anche quale sarebbe il modo corretto di implementare quello che volevo per favore?

risposta

10

Il il parametro di default nelle funzioni viene valutato solo una volta, al momento della definizione della funzione. Quindi esiste solo un'istanza di InnerTest utilizzata per entrambi gli oggetti.

Ciò significa che, quando si creano due oggetti:

a = OuterTest() 
b = OuterTest() 

Sia, a.inner_test e b.inner_test, si riferiscono alla stessa istanza, e quindi il risultato.

Per risolvere questo problema, modificare il valore predefinito per None, e creare esempio condizionale:

class OuterTest: 
    def __init__(self, inner_test=None): 
     if not inner_test: 
      inner_test = InnerTest() 
     self.inner_test = inner_test 
+1

@PadraicCunningham Non è necessario passare necessariamente un argomento. In tal caso, sarebbe 'None', e quindi è possibile inizializzarlo nella funzione. –

+1

@PadraicCunningham No, il problema non è lo stesso. Se il valore è 'None', in entrambi' a' e 'b', questo creerà 2 diverse istanze di' InnerTest', all'interno di 'if'. –

+0

Questa è la risposta corretta: spiega cosa sta succedendo e fornisce una soluzione pratica per creare istanze distinte mantenendo la comodità dei parametri predefiniti. Grazie. – marcv81

2

È possibile spostare InnerTest() nell'iniziale o chiamarlo in init, passando un riferimento alla classe n nel secondo caso.

Uso def __init__(self, inner_test = InnerTest()): viene valutata una volta così l'oggetto è condivisa tra tutte le istanze della classe OuterTest rendendolo un class attribute al contrario di un attributo di un'istanza istanziandola nella init.

class OuterTest: 
    def __init__(self): 
     self.inner_test = InnerTest() 

Oppure:

class OuterTest: 
    def __init__(self, inner_test = InnerTest): 
     self.inner_test = inner_test() 

Entrambi i metodi funzionano, se lo desideri, il superamento di un riferimento a una classe significa avere la possibilità di passare in qualsiasi classe che si desidera:

In [11]: class OuterTest: 
    ....:  def __init__(self, inner_test=InnerTest): 
    ....:    self.inner_test = inner_test() 
    ....:   

In [12]: a = OuterTest() 

In [13]: b = OuterTest() 

In [14]: a.inner_test.value = 42 

In [15]: print(a.inner_test.value) 
42 

In [16]: print(b.inner_test.value) 
0 

In [17]: class OuterTest: 
    ....:  def __init__(self): 
    ....:    self.inner_test = InnerTest() 
    ....:   

In [18]: a = OuterTest() 

In [19]: b = OuterTest() 

In [20]: a.inner_test.value = 42 

In [21]: print(a.inner_test.value) 
42 

In [22]: print(b.inner_test.value) 
+0

La seconda soluzione non è quello che voglio, ma il primo si lavora bene. Vuoi sapere perché il mio codice originale non fa quello che voglio? – marcv81

+0

Rendere facoltativo il parametro inner_text significa che potrebbe voler passare un oggetto istanziato come parametro. Funzionerebbe con la tua risposta? –

+0

@ marcv81, è essenzialmente la differenza tra una classe un attributo di istanza –