Python assegna più variabili allo stesso valore? comportamento dell'elenco


132

Ho provato a utilizzare l'assegnazione multipla come mostrato di seguito per inizializzare le variabili, ma mi sono confuso dal comportamento, mi aspetto di riassegnare l'elenco dei valori separatamente, intendo b [0] ec [0] uguale a 0 come prima.

a=b=c=[0,3,5]
a[0]=1
print(a)
print(b)
print(c)

Il risultato è: [1, 3, 5] [1, 3, 5] [1, 3, 5]

È corretto? cosa dovrei usare per compiti multipli? cosa è diverso da questo?

d=e=f=3
e=4
print('f:',f)
print('e:',e)

risultato: ('f:', 3) ('e:', 4)


2
Vuoi a, be c,per tutti i punti sullo stesso valore (in questo caso una lista), o vuoi a=0, b=3e c=5. In tal caso, vuoi a,b,c = [0,3,5]o semplicemente a,b,c = 0,3,5.
chepner,

Risposte:


271

Se arrivate a Python da una lingua in C / Java / ecc. famiglia, può aiutarti a smettere di pensare acome una "variabile" e iniziare a pensarla come un "nome".

a, be cnon sono variabili diverse con valori uguali; sono nomi diversi per lo stesso valore identico. Le variabili hanno tipi, identità, indirizzi e tutti i tipi di cose del genere.

I nomi non hanno nulla di tutto ciò. I valori sì, ovviamente, e puoi avere molti nomi per lo stesso valore.

Se dai Notorious B.I.G.un hot dog, * Biggie Smallse Chris Wallacehai un hot dog. Se si modifica il primo elemento di ain 1, i primi elementi di be csono 1.

Se vuoi sapere se due nomi stanno nominando lo stesso oggetto, usa l' isoperatore:

>>> a=b=c=[0,3,5]
>>> a is b
True

Quindi chiedi:

cosa è diverso da questo?

d=e=f=3
e=4
print('f:',f)
print('e:',e)

Qui, stai ricollegando il nome eal valore 4. Ciò non influisce sui nomi de fin alcun modo.

Nella versione precedente, stavi assegnando a a[0], non a a. Quindi, dal punto di vista di a[0], stai ribaltando a[0], ma dal punto di vista di a, lo stai cambiando sul posto.

Puoi usare la idfunzione, che ti dà un numero univoco che rappresenta l'identità di un oggetto, per vedere esattamente quale oggetto è anche quando isnon può aiutarti:

>>> a=b=c=[0,3,5]
>>> id(a)
4473392520
>>> id(b)
4473392520
>>> id(a[0])
4297261120
>>> id(b[0])
4297261120

>>> a[0] = 1
>>> id(a)
4473392520
>>> id(b)
4473392520
>>> id(a[0])
4297261216
>>> id(b[0])
4297261216

Si noti che a[0]è stato modificato da 4297261120 a 4297261216, ora è un nome per un valore diverso. Ed b[0]ora è anche un nome per lo stesso nuovo valore. Questo perché ae bstanno ancora nominando lo stesso oggetto.


Sotto le copertine, in a[0]=1realtà sta chiamando un metodo sull'oggetto elenco. (È equivalente a a.__setitem__(0, 1).) Quindi, in realtà non sta affatto ribaltando nulla. È come chiamare my_object.set_something(1). Certo, probabilmente l'oggetto sta vincolando un attributo di istanza per implementare questo metodo, ma non è questo ciò che è importante; l'importante è che non stai assegnando nulla, stai solo mutando l'oggetto. Ed è lo stesso con a[0]=1.


user570826 ha chiesto:

E se avessimo a = b = c = 10

È esattamente la stessa situazione di a = b = c = [1, 2, 3]: hai tre nomi per lo stesso valore.

Ma in questo caso, il valore è an inte ints sono immutabili. In entrambi i casi, puoi ricollegare aa un valore diverso (ad esempio, a = "Now I'm a string!"), ma ciò non influirà sul valore originale, per il quale be csaranno comunque nomi. La differenza è che con un elenco, è possibile modificare il valore [1, 2, 3]in [1, 2, 3, 4]facendo, ad esempio, a.append(4); dal momento che questo sta effettivamente cambiando il valore per cui be csono nomi, bora b [1, 2, 3, 4]. Non c'è modo di cambiare il valore 10in nient'altro. 10ha 10 anni per sempre, proprio come Claudia il vampiro ne ha 5 per sempre (almeno fino a quando non viene sostituita da Kirsten Dunst).


* Avvertenza: non dare a Notorious BIG un hot dog. Gli zombi rap Gangsta non dovrebbero mai essere nutriti dopo mezzanotte.


E se noi have, a = b = c = 10; e quando proviamo ad aggiornare il valore di b, ha effetto su qualsiasi altro? anche se ho verificato che i loro ID fossero gli stessi.
AJ

@ user570826: 10è immutabile, ciò significa che non c'è modo di aggiornare il valore, quindi la tua domanda non ha senso. Puoi indicareb a un valore diverso, ma farlo non ha alcun effetto su ae c, che stanno ancora puntando sul valore originale. La differenza che fanno gli elenchi è che sono modificabili, ad esempio, puoi fare appendun elenco o lst[0] = 3, e questo aggiornerà il valore, che sarà visibile attraverso tutti i nomi per quel valore.
abarnert,

72

Tosse tosse

>>> a,b,c = (1,2,3)
>>> a
1
>>> b
2
>>> c
3
>>> a,b,c = ({'test':'a'},{'test':'b'},{'test':'c'})
>>> a
{'test': 'a'}
>>> b
{'test': 'b'}
>>> c
{'test': 'c'}
>>> 

10
IMHO, questo in realtà risponde alla prima domanda chiave di OP su cosa dovrei usare per compiti multipli, mentre la risposta più alta e più cerebrale sopra non lo fa.
Will Croxford,

2
O a,b,c = 1,2,3senza parentesi funziona in Python 2 o 3, se vuoi davvero quel cm in più di leggibilità.
Will Croxford,

14

Sì, questo è il comportamento previsto. a, bec sono tutti impostati come etichette per lo stesso elenco. Se si desidera tre elenchi diversi, è necessario assegnarli singolarmente. Puoi ripetere l'elenco esplicito o utilizzare uno dei numerosi modi per copiare un elenco:

b = a[:] # this does a shallow copy, which is good enough for this case
import copy
c = copy.deepcopy(a) # this does a deep copy, which matters if the list contains mutable objects

Le istruzioni di assegnazione in Python non copiano gli oggetti, ma associano il nome a un oggetto e un oggetto può avere tutte le etichette impostate. Nella tua prima modifica, cambiando a [0], stai aggiornando un elemento del singolo elenco a cui fanno riferimento a, b e c. Nel secondo, cambiando e, stai cambiando e in modo che diventi un'etichetta per un oggetto diverso (4 anziché 3).


13

In Python, tutto è un oggetto, anche tipi di variabili "semplici" (int, float, ecc.).

Quando si modifica un valore di variabile, in realtà si cambia il suo puntatore e se si confronta tra due variabili si confronta i loro puntatori . (Per essere chiari, puntatore è l'indirizzo nella memoria del computer fisico in cui è memorizzata una variabile).

Di conseguenza, quando si modifica un valore di variabile interna, si modifica il valore in memoria e influisce su tutte le variabili che puntano a questo indirizzo.

Per il tuo esempio, quando lo fai:

a = b =  5 

Ciò significa che aeb punta allo stesso indirizzo in memoria che contiene il valore 5, ma quando lo fai:

a = 6

Non ha effetto su b poiché a ora punta a un'altra posizione di memoria che contiene 6 e b punta ancora all'indirizzo di memoria che contiene 5.

Ma quando lo fai:

a = b = [1,2,3]

aeb, di nuovo, punta nella stessa posizione ma la differenza è che se si modifica uno dei valori dell'elenco:

a[0] = 2

Cambia il valore della memoria su cui punta a, ma a indica ancora lo stesso indirizzo di b e, di conseguenza, cambia anche b.


6
Questo è altamente fuorviante. I puntatori non sono certamente visibili a livello di Python e almeno due delle quattro implementazioni principali (PyPy e Jython) non le usano nemmeno all'interno dell'implementazione.
abarnert,

1
Ti invitiamo a leggere ed esplorare gli interni di Python e scoprirai che ogni variabile in Python è in realtà puntatore.
Ori Seri,

4
No. In un'implementazione di Python (CPython), ogni variabile è un puntatore a PyObject. Questo non è vero in altre implementazioni come PyPy o Jython. (In effetti, non è nemmeno chiaro come potrebbe essere vero, perché i linguaggi in cui sono scritte queste implementazioni non hanno nemmeno puntatori.)
abarnert

Penso che l'uso del "puntatore" in senso concettuale sia ok (forse con una dichiarazione di non responsabilità che le implementazioni possono variare), specialmente se l'obiettivo è quello di comunicare il comportamento.
Levon,

@abarnert Quando le persone dicono Python intendono CPython, non altre implementazioni usate raramente. Proprio come quando le persone dicono Kleenex intendono tessuto facciale. Giocare al gioco della semantica in questi commenti è davvero superfluo. Quanto a ciò che ha scritto, il comportamento di ciò che ha descritto è sbagliato?
Swade

10

Puoi usare id(name)per verificare se due nomi rappresentano lo stesso oggetto:

>>> a = b = c = [0, 3, 5]
>>> print(id(a), id(b), id(c))
46268488 46268488 46268488

Le liste sono mutabili; significa che puoi cambiare il valore sul posto senza creare un nuovo oggetto. Tuttavia, dipende da come si modifica il valore:

>>> a[0] = 1
>>> print(id(a), id(b), id(c))
46268488 46268488 46268488
>>> print(a, b, c)
[1, 3, 5] [1, 3, 5] [1, 3, 5]

Se si assegna un nuovo elenco di a, allora la sua id cambierà, in modo da non influenzare be c's valori:

>>> a = [1, 8, 5]
>>> print(id(a), id(b), id(c))
139423880 46268488 46268488
>>> print(a, b, c)
[1, 8, 5] [1, 3, 5] [1, 3, 5]

I numeri interi sono immutabili, quindi non è possibile modificare il valore senza creare un nuovo oggetto:

>>> x = y = z = 1
>>> print(id(x), id(y), id(z))
507081216 507081216 507081216
>>> x = 2
>>> print(id(x), id(y), id(z))
507081248 507081216 507081216
>>> print(x, y, z)
2 1 1

1
idnon è necessariamente una posizione di memoria. Come dicono i documenti , questo restituisce "l'identità ... un numero intero ... che è garantito per essere unico e costante per questo oggetto durante la sua vita." CPython usa l'indirizzo di memoria come id, ma altre implementazioni di Python potrebbero non esserlo. PyPy, ad esempio, no. E dire "due vars indicano la stessa posizione di memoria" è fuorviante per chiunque lo capisca in stile C. "Due nomi per lo stesso oggetto" è sia più preciso che meno fuorviante.
abarnert,

@abarnert grazie per il chiarimento, ho aggiornato la risposta.
Jurgenreza,

7

nel tuo primo esempio a = b = c = [1, 2, 3]stai davvero dicendo:

 'a' is the same as 'b', is the same as 'c' and they are all [1, 2, 3]

Se vuoi impostare 'a' uguale a 1, 'b' uguale a '2' e 'c' uguale a 3, prova questo:

a, b, c = [1, 2, 3]

print(a)
--> 1
print(b)
--> 2
print(c)
--> 3

Spero che questo ti aiuti!


4

In poche parole, nel primo caso, si stanno assegnando più nomi a list. Viene creata una sola copia dell'elenco e tutti i nomi si riferiscono a quella posizione. Quindi, cambiando l'elenco usando uno qualsiasi dei nomi, si cambierà effettivamente l'elenco in memoria.

Nel secondo caso, vengono create in memoria più copie dello stesso valore. Quindi ogni copia è indipendente l'una dall'altra.


3

Ciò di cui hai bisogno è questo:

a, b, c = [0,3,5] # Unpack the list, now a, b, and c are ints
a = 1             # `a` did equal 0, not [0,3,5]
print(a)
print(b)
print(c)

3

Il codice che fa quello che mi serve potrebbe essere questo:

# test

aux=[[0 for n in range(3)] for i in range(4)]
print('aux:',aux)

# initialization

a,b,c,d=[[0 for n in range(3)] for i in range(4)]

# changing values

a[0]=1
d[2]=5
print('a:',a)
print('b:',b)
print('c:',c)
print('d:',d)

Risultato:

('aux:', [[0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0]])
('a:', [1, 0, 0])
('b:', [0, 0, 0])
('c:', [0, 0, 0])
('d:', [0, 0, 5])
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.