Подтвердить что ты не робот

Являются ли переменные 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].

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

4b9b3361

Ответ 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)

Малые ints интернированы, но это не важно для этого объяснения.

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

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

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

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

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

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 являются изменяемыми объектами, поэтому, когда вы изменяете список из одной ссылки и смотрите на него из другой ссылки, вы увидите тот же результат, потому что это тот же список.

Ответ 4

TLDR: имена 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 -+ -/     +-----------+

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

Ответ 5

Они не совсем указатели, они являются ссылками на объекты. Объекты могут быть изменчивыми или неизменными. Постоянный объект копируется при его изменении. Измененный объект изменяется на месте. Целое число - это неизменный объект, который вы ссылаетесь на переменные я и j. Список - изменяемый объект.

В первом примере

i=5
# The label i now references 5
j=i
# The label j now references what i references
j=3
# The label j now references 3
print i
# i still references 5

В вашем втором примере:

i=[1,2,3]
# i references a list object (a mutable object)
j=i
# j now references the same object as i (they reference the same mutable object)
i[0]=5
# sets first element of references object to 5
print j
# prints the list object that j references. It the same one as i.

Ответ 6

Когда вы устанавливаете j=3, метка j больше не применяется (точки) до i, она начинает указывать на целое число 3. Имя i по-прежнему относится к исходному значению, 5.

Ответ 7

какая переменная находится слева от знака '=', присваивается значение в правой части '='

i = 5

j = i --- j имеет 5

j = 3 --- j имеет 3 (перезаписывает значение 5), но ничего не изменилось относительно i

print(i) - так что это печатает 5

Ответ 8

Назначение не изменяет объекты; все, что он делает, это изменяет, куда указывает переменная. Изменение точки одной переменной не изменит, а точки другой.

Вы, вероятно, думаете о том, что списки и словари являются изменчивыми типами. Есть операторы для изменения реальных объектов на месте, и если вы используете один из них, вы увидите изменение во всех переменных, указывающих на один и тот же объект:

x = []
y = x
x.append(1)
# x and y both are now [1]

Но назначение все еще просто перемещает указатель:

x = [2]
# x now points to new list [2]; y still points to old list [1]

Числа, в отличие от словарей и списков, являются неизменными. Если вы делаете x = 3; x += 2, вы не превращаете число 3 в число 5; вы просто указываете переменную x на 5. Значение 3 все еще остается неизменным, и любые переменные, указывающие на него, все равно будут видеть значение 3 в качестве значения.

(В реальной реализации числа, вероятно, вообще не являются ссылочными типами; более вероятно, что переменные фактически содержат представление значения непосредственно, а не указывают на него. Но эта деталь реализации не меняет семантику, когда речь идет о неизменяемых типах. )

Ответ 9

В Python все объекты, включая сами части памяти, будут возвращены. Это означает, что при создании нового фрагмента памяти (независимо от того, что вы создали: int, str, пользовательский объект и т.д.), У вас есть новый объект памяти. В вашем случае это назначение 3, которое создает новый (память) объект и, следовательно, имеет новый адрес.

Если вы запустите следующее, вы увидите, что я имею в виду.

i = 5
j = i
print("id of j: {}", id(j))
j = 3
print("id of j: {}", id(j))

IMO, память мудрая, это ключевое понимание/различие между C и Python. В C/С++ вы возвращаете указатель памяти (если вы используете синтаксис указателя, конечно) вместо объекта памяти, что дает вам большую гибкость в плане изменения указанного адреса.