Dell'oggetto aggiunto a un'istanza elenco viene visualizzato in una diversa istanza di tale elenco
Domanda
stavo scrivendo questo piccolo pezzo di codice come un esercizio di programmazione orientata agli oggetti.
Qui sto cercando di definire una casa come una lista di camere e ogni camera come una lista di dispositivi (lampade, per esempio).
Per prima cosa ho creato tutti gli oggetti e le allegate le due stanze alla casa e un dispositivo diverso per ogni stanza. Piuttosto semplice.
Il problema è che sembra che il dispositivo viene aggiunto ad entrambe le camere. Perché?
Il codice:
#! /usr/bin/python
class House:
def __init__(self, rooms = list()):
self.rooms = rooms
print('house created')
class Room:
def __init__(self, name = 'a room', devs = list()):
self.name = name
self.devs = devs
print('room ' + self.name + ' created')
class Device:
def __init__(self, name = 'a device'):
self.name = name
print('device ' + self.name + ' created')
def main():
#1
h = House()
r1 = Room(name = 'R1')
r2 = Room(name = 'R2')
d1 = Device(name = 'lamp1')
d2 = Device(name = 'lamp2')
#2
h.rooms.append(r1)
h.rooms.append(r2)
for room in h.rooms:
print room.name
print h.rooms[0]
print h.rooms[1]
h.rooms[1].devs.append(d1)
#3
for room in h.rooms:
print room.name
for dev in room.devs:
print('room ' + room.name + ' > ' + dev.name)
print room
print dev
if __name__ == '__main__' : main()
E l'uscita.
house created
room R1 created
room R2 created
device lamp1 created
device lamp2 created
R1
R2
<__main__.Room instance at 0xb7d8a58c>
<__main__.Room instance at 0xb7d8a5ac>
R1
room R1 > lamp1
<__main__.Room instance at 0xb7d8a58c>
<__main__.Device instance at 0xb7d8a5cc>
R2
room R2 > lamp1
<__main__.Room instance at 0xb7d8a5ac>
<__main__.Device instance at 0xb7d8a5cc>
Si noti che la stessa istanza di d1 è in entrambe le camere, R1 e R2.
Soluzione
i valori dei parametri predefiniti per le funzioni vengono calcolate una sola volta. Ciò significa che tutte le istanze di Casa utilizzeranno la stessa istanza lista per self.rooms
(se il parametro camere non è stato dato nelle costruzioni). Allo stesso modo, tutte le istanze di camera condivideranno la stessa lista per self.devs
.
Per risolvere questo problema, scrivere il codice in questo modo:
def __init__(self, rooms = None):
if rooms is None:
rooms = []
self.rooms = rooms
print('house created')
E la stessa cosa per le altre classi.
Altri suggerimenti
L'argomento predefinito viene valutata una volta, nel punto di dichiarazione del metodo. Tale valore viene quindi utilizzato in tutte le chiamate al metodo.
Ci sono altre domande su StackOverflow esplorare le ragioni di questo disegno e come meglio evitare questi argomenti di default mutevoli .
def __init__(self, name = 'a room', devs = list()):
self.name = name
self.devs = devs
print('room ' + self.name + ' created')
Quando si esegue questa list()
in realtà è sempre la stessa lista. Non si ottiene un nuovo elenco vuoto ogni volta che il costruttore viene chiamato, si ottiene il stesso lista vuota. Per risolvere che si vorrà fare una copia.
Anche list()
è più idiomaticamente scritto come []
.
def __init__(self, name='a room', devs=[]):
self.name = name
self.devs = list(devs)
print('room ' + self.name + ' created')