TLDR: nazwy Pythona działają jak wskaźniki z automatycznym usuwaniem / odwołaniem, ale nie pozwalają na jawne operacje wskaźnikowe. Inne cele reprezentują pośrednie, które zachowują się podobnie do wskaźników.
Implementacja CPython używa wskaźników typuPyObject* pod maską. W związku z tym możliwe jest przetłumaczenie semantyki nazw na operacje wskaźnikowe. Kluczem jest oddzielenie nazw od rzeczywistych obiektów .
Przykładowy kod w Pythonie zawiera zarówno nazwy ( i), jak i obiekty ( 5).
i = 5
j = i
j = 3
Można to z grubsza przetłumaczyć na kod C z oddzielnymi nazwami i obiektami.
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`
Ważną częścią jest to, że „nazwy-wskaźniki” nie przechowują obiektów! Nie zdefiniowaliśmy *i = five, ale i = &five. Nazwy i przedmioty istnieją niezależnie od siebie.
Nazwy wskazują tylko na istniejące obiekty w pamięci.
Podczas przypisywania nazwy do nazwy żadne obiekty nie są wymieniane! Kiedy definiujemy j = i, jest to równoważne z j = &five. Ani jedno, iani jdrugie nie jest połączone.
+- name i -+ -\
\
--> + <five> -+
/ | 5 |
+- name j -+ -/ +----------+
W rezultacie zmiana celu jednej nazwy nie wpływa na drugą . Aktualizuje tylko to, na co wskazuje ta konkretna nazwa.
Python ma również inne rodzaje elementów przypominających nazwy : odwołania do atrybutów ( i.j), subscriptions ( i[j]) i slicing ( i[:j]). W przeciwieństwie do nazw, które odnoszą się bezpośrednio do przedmiotów, wszystkie trzy odnoszą się pośrednio do elementów przedmiotów.
Przykładowy kod zawiera obie nazwy ( i) i subskrypcję ( i[0]).
i = [1,2,3]
j = i
i[0] = 5
CPython listużywa tablicy PyObject*wskaźników w C pod maską. Można to z grubsza przetłumaczyć na kod C z oddzielnymi nazwami i obiektami.
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`
Ważne jest to, że nie zmieniliśmy żadnych nazw! Zmieniliśmy i->elements[0]element obiektu, na który wskazują nasze nazwy.
Można zmieniać wartości istniejących obiektów złożonych.
Podczas zmiany wartości obiektu poprzez nazwę nazwy nie ulegają zmianie. Oba ii jnadal odnoszą się do tego samego obiektu, którego wartość możemy zmienić.
+- name i -+ -\
\
--> + <values> -+
/ | elements | --> [1, 2, 3]
+- name j -+ -/ +-----------+
Obiekt pośredni zachowuje się podobnie do wskaźnika, ponieważ możemy bezpośrednio zmienić to, na co wskazuje i odwoływać się do niego z wielu nazw.