TLDR: Імена Python працюють як вказівники з автоматичним відключенням / посиланням, але не дозволяють явних операцій вказівника. Інші цілі представляють опосередкованість, яка поводиться подібно до покажчиків.
Реалізація CPython використовує покажчики типуPyObject*
під капотом. Таким чином, можна перевести семантику імен на операції покажчика. Головне - відокремити імена від реальних об’єктів .
Приклад коду Python включає як імена ( i
), так і об'єкти ( 5
).
i = 5
j = i
j = 3
Це можна приблизно перекласти на код C з окремими іменами та об'єктами.
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`
Важливим є те, що "імена як покажчики" не зберігають об'єкти! Ми не визначали *i = five
, але i = &five
. Назви та предмети існують незалежно один від одного.
Імена вказують лише на наявні об'єкти в пам'яті.
При присвоєнні від імені до імені ніякі об'єкти не обмінюються! Коли ми визначаємо j = i
, це еквівалентно j = &five
. Ні ті, i
ні інші j
не пов'язані.
+- name i -+ -\
\
--> + <five> -+
/ | 5 |
+- name j -+ -/ +----------+
Як результат, зміна цілі одного імені не впливає на інше . Він лише оновлює те, на що вказує ця конкретна назва.
Python також має інші види елементів, подібних до імен : посилання на атрибути ( i.j
), підписки ( i[j]
) та нарізання ( i[:j]
). На відміну від імен, що стосуються безпосередньо об’єктів, усі три опосередковано стосуються елементів об’єктів.
Приклад коду включає як імена ( i
), так і підписку ( i[0]
).
i = [1,2,3]
j = i
i[0] = 5
CPython list
використовує масив PyObject*
покажчиків C під капотом. Це знову можна приблизно перекласти на код C з окремими іменами та об'єктами.
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`
Важливим є те, що ми не змінювали жодної назви! Ми все-таки змінились i->elements[0]
, елемент об’єкта, на який вказують наші імена.
Значення існуючих складених об'єктів можуть бути змінені.
При зміні значення об’єкта через ім’я імена не змінюються. Обидва i
і j
все ще стосуються одного і того ж об’єкта, значення якого ми можемо змінити.
+- name i -+ -\
\
--> + <values> -+
/ | elements | --> [1, 2, 3]
+- name j -+ -/ +-----------+
Проміжний об'єкт поводиться подібно до вказівника, оскільки ми можемо безпосередньо змінювати те, на що він вказує, і посилатися на нього з кількох імен.