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, i
ani j
drugie 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 list
uż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 i
i j
nadal 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.