Variablen in Python sind meines Wissens nur Zeiger.
Basierend auf dieser Regel kann ich davon ausgehen, dass das Ergebnis für dieses Code-Snippet:
i = 5
j = i
j = 3
print(i)
wäre 3
. Aber ich habe ein unerwartetes Ergebnis für mich erzielt 5
.
Außerdem behandelt mein Python-Buch dieses Beispiel:
i = [1,2,3]
j = i
i[0] = 5
print(j)
das Ergebnis wäre [5,2,3]
.
Was verstehe ich falsch?
i
es gleich sein sollte3
Antworten:
Wir nennen sie Referenzen. Sie arbeiten so
i = 5 # create int(5) instance, bind it to i j = i # bind j to the same int as i j = 3 # create int(3) instance, bind it to j print i # i still bound to the int(5), j bound to the int(3)
Kleine Ints werden interniert, aber das ist für diese Erklärung nicht wichtig
i = [1,2,3] # create the list instance, and bind it to i j = i # bind j to the same list as i i[0] = 5 # change the first item of i print j # j is still bound to the same list as i
quelle
40
verweisen Sie bei jeder Eingabe auf dasselbe Objekt im Speicher. Um diesen Typ zu sehen, geben Sie a, b = 256 ein und testen Siea is b
. Versuchen Sie es jetzt mit a, b = 257. Siehe: stackoverflow.com/a/1136852/3973834 und codementor.io/python/tutorial/…Variablen sind keine Zeiger. Wenn Sie einer Variablen zuweisen, binden Sie den Namen an ein Objekt. Ab diesem Zeitpunkt können Sie mit dem Namen auf das Objekt verweisen, bis dieser Name zurückgebunden wird.
In Ihrem ersten Beispiel ist der Name
i
an den Wert gebunden5
. Das Binden verschiedener Werte an den Namenj
hat keine Auswirkungen aufi
. Wenn Sie später drucken, bleibt der Wert desi
Werts erhalten5
.In Ihrem zweiten Beispiel binden Sie beide
i
undj
an dasselbe Listenobjekt. Wenn Sie den Inhalt der Liste ändern, können Sie die Änderung unabhängig davon sehen, mit welchem Namen Sie auf die Liste verweisen.Beachten Sie, dass es falsch wäre, wenn Sie sagen würden, dass sich beide Listen geändert haben. Es gibt nur eine Liste, aber es gibt zwei Namen (
i
undj
), die darauf verweisen.Dazugehörige Dokumentation
quelle
Python-Variablen sind Namen, die an Objekte gebunden sind
Aus den Dokumenten :
Wenn Sie das tun
i = 5 j = i
das ist das gleiche wie:
i = 5 j = 5
j
zeigt nicht aufi
undj
weiß nach der Zuweisung nicht, dass esi
existiert.j
ist einfach an das gebunden, woraufi
zum Zeitpunkt der Zuweisung hingewiesen wurde.Wenn Sie die Aufgaben in derselben Zeile erledigen würden, würde dies folgendermaßen aussehen:
i = j = 5
Und das Ergebnis wäre genau das gleiche.
Also später machen
i = 3
ändert nicht, worauf
j
zeigt - und Sie können es tauschen -j = 3
würde nicht ändern, woraufi
zeigt.In Ihrem Beispiel wird der Verweis auf die Liste nicht entfernt
Wenn Sie dies tun:
i = [1,2,3] j = i
Es ist das gleiche wie das:
i = j = [1,2,3]
so
i
undj
beide zeigen auf die gleiche Liste. Dann mutiert Ihr Beispiel die Liste:i[0] = 5
Python-Listen sind veränderbare Objekte. Wenn Sie also die Liste von einer Referenz aus ändern und von einer anderen Referenz aus betrachten, sehen Sie dasselbe Ergebnis, da es sich um dieselbe Liste handelt.
quelle
TLDR: Python- Namen funktionieren wie Zeiger mit automatischer De- / Referenzierung, erlauben jedoch keine expliziten Zeigeroperationen. Andere Ziele stellen Indirektionen dar, die sich ähnlich wie Zeiger verhalten.
Die CPython-Implementierung verwendet Zeiger vom Typ
PyObject*
unter der Haube. Daher ist es möglich, Namenssemantik in Zeigeroperationen zu übersetzen. Der Schlüssel besteht darin, Namen von tatsächlichen Objekten zu trennen .Der Beispiel-Python-Code enthält sowohl Namen (
i
) als auch Objekte (5
).i = 5 # name `i` refers to object `5` j = i # ??? j = 3 # name `j` refers to object `3`
Dies kann grob in C-Code mit separaten Namen und Objekten übersetzt werden.
int three=3, five=5; // objects int *i, *j; // names i = &five; // name `i` refers to position of object `5` j = i; // name `j` refers to referent of `i` j = &three; // name `j` refers to position of object `3`
Der wichtige Teil ist, dass "Namen als Zeiger" keine Objekte speichern! Wir haben nicht definiert
*i = five
, aberi = &five
. Die Namen und Objekte existieren unabhängig voneinander.Namen verweisen nur auf vorhandene Objekte im Speicher.
Bei der Zuweisung von Name zu Name werden keine Objekte ausgetauscht! Wenn wir definieren
j = i
, ist dies äquivalent zuj = &five
. Wederi
nochj
sind mit dem anderen verbunden.+- name i -+ -\ \ --> + <five> -+ / | 5 | +- name j -+ -/ +----------+
Das Ändern des Ziels eines Namens wirkt sich daher nicht auf den anderen aus . Es wird nur aktualisiert, worauf dieser bestimmte Name verweist.
Python hat auch andere Arten von namenähnlichen Elementen : Attributreferenzen (
i.j
), Abonnements (i[j]
) und Slicing (i[:j]
). Im Gegensatz zu Namen, die sich direkt auf Objekte beziehen, beziehen sich alle drei indirekt auf Elemente von Objekten.Der Beispielcode enthält sowohl Namen (
i
) als auch ein Abonnement (i[0]
).i = [1,2,3] # name `i` refers to object `[1, 2, 3]` j = i # name `j` refers to referent of `i` i[0] = 5 # ???
Ein CPython
list
verwendet ein C-Array vonPyObject*
Zeigern unter der Haube. Dies kann wiederum grob in C-Code mit separaten Namen und Objekten übersetzt werden.typedef struct{ int *elements[3]; } list; // length 3 `list` type int one = 1, two = 2, three = 3, five = 5; list values = {&one, &two, &three}; // objects list *i, *j; // names i = &values; // name `i` refers to object `[1, 2, 3]` j = i; // name `j` refers to referent of `i` i->elements[0] = &five; // leading element of `i` refers to object `5`
Wichtig ist, dass wir keine Namen geändert haben! Wir haben
i->elements[0]
das Element eines Objekts geändert , auf das unsere beiden Namen verweisen.Die Werte vorhandener zusammengesetzter Objekte können geändert werden.
Wenn Sie den Wert eines Objekts über einen Namen ändern, werden die Namen nicht geändert. Beide
i
undj
beziehen sich immer noch auf dasselbe Objekt, dessen Wert wir ändern können.+- name i -+ -\ \ --> + <values> -+ / | elements | --> [1, 2, 3] +- name j -+ -/ +-----------+
Das Zwischenobjekt verhält sich insofern ähnlich wie ein Zeiger, als wir direkt ändern können, worauf es zeigt, und es aus mehreren Namen referenzieren können.
quelle
i
undj
Zuweisungen in Ihrem Beispiel. Sie beginnen miti = 5
,j = 3
und sie dann in den Rest Ihres Post invertieren. Dies ist jedoch die einzige Antwort, die der Frage im OP gerecht wird und wirklich erklärt, was unter der Haube passiert.Sie sind keine Zeiger, sondern Verweise auf Objekte. Objekte können entweder veränderlich oder unveränderlich sein. Ein unveränderliches Objekt wird kopiert, wenn es geändert wird. Ein veränderliches Objekt wird an Ort und Stelle geändert. Eine Ganzzahl ist ein unveränderliches Objekt, auf das Sie mit Ihren Variablen i und j verweisen. Eine Liste ist ein veränderbares Objekt.
In Ihrem ersten Beispiel
i=5 # The label i now references 5 j=i # The label j now references what i references j=3 # The label j now references 3 print i # i still references 5
In Ihrem zweiten Beispiel:
i=[1,2,3] # i references a list object (a mutable object) j=i # j now references the same object as i (they reference the same mutable object) i[0]=5 # sets first element of references object to 5 print j # prints the list object that j references. It's the same one as i.
quelle
Wenn Sie
j=3
die Beschriftung festlegenj
nicht mehr gilt (Punkte),i
zeigt sie auf die Ganzzahl3
. Der Namei
bezieht sich immer noch auf den ursprünglich festgelegten Wert5
.quelle
Jede Variable auf der linken Seite des Zeichens '=' wird mit dem Wert auf der rechten Seite von '=' versehen.
i = 5
j = i
--- j hat 5j = 3
--- j hat 3 (überschreibt den Wert von 5), aber in Bezug auf i wurde nichts geändertprint(i)
- so druckt dies 5quelle
Durch die Zuweisung werden keine Objekte geändert. Alles, was es tut, ist zu ändern, wo die Variable zeigt. Wenn Sie ändern, wo eine Variable zeigt, ändert sich nicht, wo eine andere zeigt.
Sie denken wahrscheinlich an die Tatsache, dass Listen und Wörterbücher veränderbare Typen sind. Es gibt Operatoren, mit denen die tatsächlichen Objekte an Ort und Stelle geändert werden können. Wenn Sie eines dieser Objekte verwenden, wird die Änderung in allen Variablen angezeigt, die auf dasselbe Objekt verweisen:
x = [] y = x x.append(1) # x and y both are now [1]
Aber die Zuweisung bewegt immer noch nur den Zeiger:
x = [2] # x now points to new list [2]; y still points to old list [1]
Zahlen sind im Gegensatz zu Wörterbüchern und Listen unveränderlich. Wenn Sie dies tun
x = 3; x += 2
, wandeln Sie die Nummer 3 nicht in die Nummer 5 um. Sie machenx
stattdessen nur den variablen Punkt auf 5. Die 3 ist immer noch unverändert da draußen, und alle Variablen, die darauf zeigen, sehen immer noch 3 als ihren Wert.(In der tatsächlichen Implementierung sind Zahlen wahrscheinlich überhaupt keine Referenztypen. Es ist wahrscheinlicher, dass die Variablen tatsächlich eine Darstellung des Werts direkt enthalten, anstatt darauf zu verweisen. Dieses Implementierungsdetail ändert jedoch nichts an der Semantik, wenn es um unveränderliche Typen geht .)
quelle
In Python ist alles ein Objekt, einschließlich der Speicherelemente selbst, die Sie zurückgeben. Das heißt, wenn ein neuer Speicherblock erstellt wird (unabhängig davon, was Sie erstellt haben: int, str, benutzerdefiniertes Objekt usw.), haben Sie ein neues Speicherobjekt. In Ihrem Fall ist dies die Zuordnung zu 3, die ein neues (Speicher-) Objekt erstellt und somit eine neue Adresse hat.
Wenn Sie Folgendes ausführen, sehen Sie leicht, was ich meine.
i = 5 j = i print("id of j: {}", id(j)) j = 3 print("id of j: {}", id(j))
IMO, was das Gedächtnis betrifft, ist dies das Schlüsselverständnis / der Unterschied zwischen C und Python. In C / C ++ erhalten Sie anstelle eines Speicherobjekts einen Speicherzeiger (wenn Sie natürlich die Zeigersyntax verwenden), der Ihnen mehr Flexibilität beim Ändern der angegebenen Adresse bietet.
quelle