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 TypPyObject* 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
j = i
j = 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, aber i = &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 zu j = &five. Weder inoch jsind 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]
j = i
i[0] = 5
Ein CPython listverwendet ein C-Array von PyObject*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 iund jbeziehen 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.