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 i
noch j
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]
j = i
i[0] = 5
Ein CPython list
verwendet 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 i
und j
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.