Python, der mehrere Variablen demselben Wert zuweist? Listenverhalten

131

Ich habe versucht, mehrere Zuweisungen wie unten gezeigt zu verwenden, um Variablen zu initialisieren, aber ich war durch das Verhalten verwirrt. Ich erwarte, die Werteliste separat neu zuzuweisen. Ich meine, b [0] und c [0] sind wie zuvor gleich 0.

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

Ergebnis ist: [1, 3, 5] [1, 3, 5] [1, 3, 5]

Ist das korrekt? Was soll ich für Mehrfachzuweisungen verwenden? Was ist anders daran?

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

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

Marco
quelle
2
Sie möchten a, bund c,an alle auf den gleichen Wert (in diesem Fall eine Liste), oder wollen Sie a=0, b=3und c=5. In diesem Fall möchten Sie a,b,c = [0,3,5]oder nur a,b,c = 0,3,5.
Chepner

Antworten:

271

Wenn Sie aus einer Sprache in C / Java / etc. Zu Python kommen. Familie, es kann Ihnen helfen, nicht mehr aals "Variable" zu denken und es als "Namen" zu betrachten.

a,, bund csind keine unterschiedlichen Variablen mit gleichen Werten; Es sind verschiedene Namen für denselben identischen Wert. Variablen haben Typen, Identitäten, Adressen und dergleichen.

Namen haben nichts davon. Werte tun dies natürlich, und Sie können viele Namen für denselben Wert haben.

Wenn Sie Notorious B.I.G.einen Hot Dog geben * Biggie Smallsund Chris Wallaceeinen Hot Dog haben. Wenn Sie das erste Element von ain 1 ändern , sind die ersten Elemente von bund c1.

Wenn Sie wissen möchten, ob zwei Namen dasselbe Objekt benennen, verwenden Sie den isOperator:

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

Sie fragen dann:

Was ist anders daran?

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

Hier binden Sie den Namen erneut ean den Wert 4. Das hat keinen Einfluss auf die Namen dund fin keiner Weise.

In Ihrer vorherigen Version haben Sie zugewiesen a[0], nicht zu a. Aus der Sicht von a[0]binden Sie also neu a[0], aber aus der Sicht von aändern Sie es an Ort und Stelle.

Sie können die idFunktion verwenden, die Ihnen eine eindeutige Nummer gibt, die die Identität eines Objekts darstellt, um genau zu sehen, welches Objekt welches ist, auch wenn isdies nicht helfen kann:

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

Beachten Sie, dass a[0]sich dieser Wert von 4297261120 in 4297261216 geändert hat. Es handelt sich nun um einen Namen für einen anderen Wert. Und b[0]ist jetzt auch ein Name für den gleichen neuen Wert. Das liegt daran aund bbenennt immer noch dasselbe Objekt.


Unter dem Deckmantel a[0]=1wird tatsächlich eine Methode für das Listenobjekt aufgerufen. (Es ist gleichbedeutend mit a.__setitem__(0, 1).) Es bindet also überhaupt nichts wirklich neu. Es ist wie ein Anruf my_object.set_something(1). Sicher, wahrscheinlich bindet das Objekt ein Instanzattribut neu, um diese Methode zu implementieren, aber das ist nicht wichtig. Wichtig ist, dass Sie nichts zuweisen, sondern nur das Objekt mutieren. Und so ist es auch mit a[0]=1.


user570826 fragte:

Was ist, wenn wir haben, a = b = c = 10

Das ist genau die gleiche Situation wie a = b = c = [1, 2, 3]: Sie haben drei Namen für den gleichen Wert.

In diesem Fall ist der Wert jedoch ein intund ints sind unveränderlich. In jedem Fall können Sie rebind aauf einen anderen Wert (zB a = "Now I'm a string!"), aber das wird nicht den ursprünglichen Wert beeinflussen, was bund cnoch sein wird Namen. Der Unterschied besteht darin, dass Sie mit einer Liste den Wert [1, 2, 3]in ändern können[1, 2, 3, 4] indem zB a.append(4); da dies tatsächlich den Wert ändert, für den bund cNamen sind, bwird nun b [1, 2, 3, 4]. Es gibt keine Möglichkeit, den Wert 10in etwas anderes zu ändern . 10ist für immer 10, genau wie Claudia ist der Vampir für immer 5 (zumindest bis sie durch Kirsten Dunst ersetzt wird).


* Warnung: Geben Sie Notorious BIG keinen Hot Dog. Gangsta-Rap-Zombies sollten niemals nach Mitternacht gefüttert werden.

abarnert
quelle
Was ist, wenn wir have, a = b = c = 10;und wenn wir versuchen, den Wert von b zu aktualisieren, wirkt sich dies auf andere aus? obwohl ich überprüft habe, dass ihre IDs gleich sind.
AJ
@ user570826: 10ist unveränderlich - das heißt, es gibt keine Möglichkeit, den Wert zu aktualisieren, sodass Ihre Frage keinen Sinn ergibt. Sie können bauf einen anderen Wert zeigen, dies hat jedoch keine Auswirkungen auf aund c, die immer noch auf den ursprünglichen Wert zeigen. Der Unterschied, den Listen machen, besteht darin, dass sie veränderbar sind - z. B. können Sie appendeine Liste lst[0] = 3erstellen oder , und das aktualisiert den Wert, der durch alle Namen für diesen Wert sichtbar ist.
abarnert
72

Hust hust

>>> 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'}
>>> 
Jimmy Kane
quelle
10
IMHO, dies beantwortet tatsächlich OP erste Schlüsselfrage, was ich für Mehrfachzuweisungen verwenden soll, während die höher bewertete und zerebralere Antwort oben dies nicht tut.
Will Croxford
2
Oder a,b,c = 1,2,3ohne Klammern funktioniert in Python 2 oder 3, wenn Sie wirklich diese zusätzliche cm Lesbarkeit wollen.
Will Croxford
14

Ja, das ist das erwartete Verhalten. a, b und c werden alle als Beschriftungen für dieselbe Liste festgelegt. Wenn Sie drei verschiedene Listen möchten, müssen Sie diese einzeln zuweisen. Sie können entweder die explizite Liste wiederholen oder eine der zahlreichen Möglichkeiten zum Kopieren einer Liste verwenden:

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

Zuweisungsanweisungen in Python kopieren keine Objekte - sie binden den Namen an ein Objekt, und ein Objekt kann so viele Beschriftungen haben, wie Sie festlegen. Bei Ihrer ersten Bearbeitung, bei der Sie a [0] ändern, aktualisieren Sie ein Element der einzelnen Liste, auf die sich a, b und c beziehen. In Ihrem zweiten, wenn Sie e ändern, ändern Sie e als Beschriftung für ein anderes Objekt (4 statt 3).

Peter DeGlopper
quelle
13

In Python ist alles ein Objekt, auch "einfache" Variablentypen (int, float usw.).

Wenn Sie einen Variablenwert ändern, ändern Sie tatsächlich seinen Zeiger , und wenn Sie zwischen zwei Variablen vergleichen, werden deren Zeiger verglichen . (Um klar zu sein, Zeiger ist die Adresse im physischen Computerspeicher, in der eine Variable gespeichert ist).

Wenn Sie also einen inneren Variablenwert ändern, ändern Sie dessen Wert im Speicher und wirken sich auf alle Variablen aus, die auf diese Adresse verweisen.

Für Ihr Beispiel, wenn Sie Folgendes tun:

a = b =  5 

Dies bedeutet, dass a und b auf dieselbe Adresse im Speicher zeigen, die den Wert 5 enthält, aber wenn Sie dies tun:

a = 6

Es hat keinen Einfluss auf b, da a jetzt auf einen anderen Speicherort zeigt, der 6 enthält, und b immer noch auf die Speicheradresse zeigt, die 5 enthält.

Aber wenn Sie dies tun:

a = b = [1,2,3]

a und b zeigen wieder auf dieselbe Position, aber der Unterschied besteht darin, dass, wenn Sie einen der Listenwerte ändern:

a[0] = 2

Es ändert den Wert des Speichers, auf den a zeigt, aber a zeigt immer noch auf dieselbe Adresse wie b, und infolgedessen ändert sich auch b.

Ori Seri
quelle
6
Das ist sehr irreführend. Zeiger sind auf Python-Ebene sicherlich nicht sichtbar, und mindestens zwei der vier Hauptimplementierungen (PyPy und Jython) verwenden sie nicht einmal innerhalb der Implementierung.
Abarnert
1
Sie können gerne Python-Interna lesen und erkunden und feststellen, dass jede Variable in Python tatsächlich ein Zeiger ist.
Ori Seri
4
Nein. In einer Implementierung von Python (CPython) ist jede Variable ein Zeiger auf a PyObject. Dies gilt nicht für andere Implementierungen wie PyPy oder Jython. (In der Tat ist es nicht einmal klar , wie es könnte wahr sein, weil die Sprachen diese Implementierungen geschrieben in Zeiger haben nicht einmal.)
abarnert
Ich denke, die Verwendung von "Zeiger" im konzeptionellen Sinne ist in Ordnung (möglicherweise mit einem Haftungsausschluss, dass Implementierungen variieren können), insbesondere wenn das Ziel darin besteht, Verhalten zu vermitteln.
Levon
@abarnert Wenn Leute Python sagen, meinen sie CPython, nicht andere selten verwendete Implementierungen. Genau wie wenn Leute Kleenex sagen, meinen sie Gesichtstuch. Das Spielen des Semantikspiels in diesen Kommentaren ist wirklich unnötig. Was das angeht, was er geschrieben hat, ist das Verhalten dessen, was er beschrieben hat, falsch?
Swade
10

Mit können Sie id(name)überprüfen, ob zwei Namen dasselbe Objekt darstellen:

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

Listen sind veränderlich; Dies bedeutet, dass Sie den vorhandenen Wert ändern können, ohne ein neues Objekt zu erstellen. Dies hängt jedoch davon ab, wie Sie den Wert ändern:

>>> 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]

Wenn Sie eine neue Liste zuweisen a, ändert sich ihre ID, sodass sie die Werte von bund nicht beeinflusst c:

>>> 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]

Ganzzahlen sind unveränderlich, sodass Sie den Wert nicht ändern können, ohne ein neues Objekt zu erstellen:

>>> 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
jurgenreza
quelle
1
idist nicht unbedingt ein Speicherort. Wie die Dokumente sagen, gibt dies die "Identität ... eine Ganzzahl ... zurück, die für dieses Objekt während seiner Lebensdauer garantiert eindeutig und konstant ist". CPython verwendet zufällig die Speicheradresse als id, andere Python-Implementierungen jedoch möglicherweise nicht. PyPy zum Beispiel nicht. Und zu sagen, "zwei Vars zeigen auf denselben Speicherort", ist für jeden, der es im C-Stil versteht, irreführend. "Zwei Namen für dasselbe Objekt" ist genauer und weniger irreführend.
Abarnert
@abarnert danke für die Klarstellung, ich habe die Antwort aktualisiert.
Jurgenreza
7

In Ihrem ersten Beispiel a = b = c = [1, 2, 3]sagen Sie wirklich:

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

Wenn Sie 'a' gleich 1, 'b' gleich '2' und 'c' gleich 3 setzen möchten, versuchen Sie Folgendes:

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

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

Hoffe das hilft!

Nick Burns
quelle
4

Einfach ausgedrückt, im ersten Fall weisen Sie a mehrere Namen zu list . Es wird nur eine Kopie der Liste im Speicher erstellt, und alle Namen beziehen sich auf diesen Speicherort. Wenn Sie also die Liste mit einem der Namen ändern, wird die Liste im Speicher geändert.

Im zweiten Fall werden mehrere Kopien desselben Werts im Speicher erstellt. Jede Kopie ist also unabhängig voneinander.

Vikas
quelle
3

Was Sie brauchen, ist Folgendes:

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)
Pydsigner
quelle
3

Der Code, der das tut, was ich brauche, könnte folgender sein:

# 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)

Ergebnis:

('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])
Marco
quelle