Вопрос-Ответ

Are Python variables pointers? Or else, what are they?

Являются ли переменные Python указателями? Или же, что это такое?

Переменные в Python - это просто указатели, насколько я знаю.

Основываясь на этом правиле, я могу предположить, что результат для этого фрагмента кода:

i = 5
j = i
j = 3
print(i)

было бы 3.

Но я получил неожиданный для меня результат, и это был 5.

Более того, в моей книге по Python действительно рассматривается этот пример:

i = [1,2,3]
j = i
i[0] = 5
print(j)

Результатом будет [5,2,3].

Что я неправильно понимаю?

Переведено автоматически
Ответ 1

Мы называем их ссылками. Они работают следующим образом

i = 5     # create int(5) instance, bind it to i
j = i # bind j to the same int as i
j = 3 # create int(3) instance, bind it to j
print i # i still bound to the int(5), j bound to the int(3)

Небольшие целые числа интернированы, но это не важно для данного объяснения

i = [1,2,3]   # create the list instance, and bind it to i
j = i # bind j to the same list as i
i[0] = 5 # change the first item of i
print j # j is still bound to the same list as i
Ответ 2

Переменные не являются указателями. Когда вы присваиваете переменной, вы привязываете имя к объекту. С этого момента вы можете ссылаться на объект, используя имя, пока это имя не восстановится.

В вашем первом примере имя i привязано к значению 5. Привязка разных значений к имени j не оказывает никакого влияния на i, поэтому, когда вы позже печатаете значение i, значение остается неизменным 5.

Во втором примере вы привязываете оба i и j к одному объекту списка. Когда вы изменяете содержимое списка, вы можете видеть изменения независимо от того, какое имя вы используете для ссылки на список.

Обратите внимание, что было бы неверно, если бы вы сказали "оба списка изменились". Существует только один список, но у него есть два имени (i и j), которые ссылаются на него.

Соответствующая документация

Ответ 3

TLDR: Имена Python работают как указатели с автоматическим удалением ссылок, но не допускают явных операций с указателями. Другие целевые объекты представляют собой косвенные указания, которые ведут себя аналогично указателям.


Спецификация языка Python не определяет, какие имена и тому подобное на самом деле есть, только как они себя ведут. Однако поведение можно объяснить с помощью указателей.

Реализация CPython использует указатели типа PyObject* под капотом. Таким образом, можно преобразовать семантику имен в операции с указателями. Ключ в том, чтобы отделить имена от реальных объектов.

Пример кода Python включает как имена (i), так и объекты (5).

i = 5  # name `i` refers to object `5`
j = i # ???
j = 3 # name `j` refers to object `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]  # name `i` refers to object `[1, 2, 3]`
j = i # name `j` refers to referent of `i`
i[0] = 5 # ???

CPython list использует C массив PyObject* указателей под капотом. Это снова можно грубо перевести в 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 -+ -/ +-----------+

Промежуточный объект ведет себя аналогично указателю в том смысле, что мы можем напрямую изменять то, на что он указывает, и ссылаться на него из нескольких имен.

Ответ 4

Переменные Python - это имена, привязанные к объектам

Из документов:


Имена относятся к объектам. Имена вводятся с помощью операций привязки имен. Каждое вхождение имени в текст программы ссылается на привязку этого имени, установленную во внутреннем функциональном блоке , содержащем use.


Когда вы делаете

i = 5
j = i

это то же самое, что делать:

i = 5
j = 5

j не указывает на i, и после присвоения, j не знает, что i существует. j просто привязывается к тому, на что i указывалось во время присвоения.

Если бы вы выполняли назначения в одной строке, это выглядело бы так:

i = j = 5

И результат был бы точно таким же.

Таким образом, позже мы сделаем

i = 3

не меняет того, на что j указывает - и вы можете поменять его местами - j = 3 не изменит того, на что i указывает.

В вашем примере ссылка на список не удаляется

Итак, когда вы делаете это:

i = [1,2,3]
j = i

Это то же самое, что делать это:

i = j = [1,2,3]

итак, i и j оба указывают на один и тот же список. Тогда ваш пример изменяет список:

i[0] = 5

Списки Python являются изменяемыми объектами, поэтому, когда вы меняете список из одной ссылки и просматриваете его из другой ссылки, вы увидите тот же результат, потому что это тот же список.

Что вам, вероятно, нужно, так это копия списка, возможно, вот так:

i = [1,2,3]
j = i.copy()

Обратите внимание, что оба списка содержат одни и те же объекты, и если они изменяемы, они будут находиться в одном и том же измененном состоянии при доступе из обоих списков, потому что это одни и те же объекты.

python