Если две переменные указывают на один и тот же объект, почему переназначение одной переменной не влияет на другую? - PullRequest
2 голосов
/ 19 июня 2019

Я пытаюсь понять, как переменные работают в Python.Скажем, у меня есть объект, хранящийся в переменной a:

>>> a = [1, 2, 3]

Если я присваиваю a b, оба указывают на один и тот же объект:

>>> b = a
>>> b is a
True

Ноесли я переназначу a или b, это уже не так:

>>> a = {'x': 'y'}
>>> a is b
False

Две переменные теперь имеют разные значения:

>>> a
{'x': 'y'}
>>> b
[1, 2, 3]

Я не понимаю, почему переменныесейчас разные.Почему a is b больше не соответствует действительности?Может кто-нибудь объяснить, что происходит?

Ответы [ 4 ]

7 голосов
/ 19 июня 2019

Python имеет имена , которые относятся к объектам . Объекты существуют отдельно от имен, а имена существуют отдельно от объектов, на которые они ссылаются.

# name a
a = 1337
    # object 1337

При назначении «имени имени» правая часть равна оценивается для упомянутого объекта. Аналогично тому, как 2 + 2 оценивается как 4, a соответствует исходному 1337.

# name b
b = a
    # object referred to by a -> 1337

На данный момент у нас есть a -> 1337 и b -> 1337 - обратите внимание, что ни одно имя не знает другого! Если мы тестируем a is b, оба имени оцениваются для одного и того же объекта, который, очевидно, равен.

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

# name a - reassign
a = 9001
  # object 9001

На данный момент у нас есть a -> 9001 и b -> 1337. Если мы сейчас проверим a is b, оба имени будут оценены для разных объектов, которые не совпадают.


Если вы пришли из таких языков, как C, то вы привыкли к переменным , содержащим значения. Например, char a = 12 можно прочитать как «a - область памяти, содержащая 12». Кроме того, вы можете иметь несколько переменных, использующих одну и ту же память. Присвоение другого значения переменной изменяет содержимое разделяемой памяти - и, следовательно, значение обеих переменных.

+- char a -+
|       12 |
+--char b -+

# a = -128

+- char a -+
|     -128 |
+--char b -+

Это не то, как работает Python: имена не содержат ничего, но относятся к отдельным значениям. Например, a = 12 можно прочитать как «a - это имя, которое относится к значению 12». Кроме того, вы можете иметь несколько имен, ссылающихся на одно и то же значение, но это будут отдельные имена, каждое со своей ссылкой. Присвоение другого значения имени изменяет ссылку на это имя, но оставляет ссылку на другое имя нетронутой.

+- name a -+ -\
               \
                --> +- <12> ---+
               /    |       12 |
+- name b -+ -/     +----------+

# a = -128
                    +- <-128> -+
+- name a -+ -----> |     -128 |
                    +----------+

                    +- <12> ---+
+- name b -+ -----> |       12 |
                    +----------+
3 голосов
/ 19 июня 2019

" Скажем, у меня есть объект, хранящийся в переменной a " - это то, где вы идете не так.

Объекты Python не сохраняются в переменных,они относятся к переменными.

a = [1, 2, 3]
b = a

a и b относятся к одному и тому же объекту.У объекта list число ссылок равно 2, поскольку на него ссылаются два имени.

a = {'x': 'y'}

a больше не ссылается на один и тот же объект list,вместо этого он теперь ссылается на dict объект.Это уменьшает счетчик ссылок на объект list, но b по-прежнему ссылается на него, поэтому счетчик ссылок объекта теперь равен 1.

b = None

Это означает, что b теперь относится к None объект (который имеет очень большое количество ссылок, многие имена ссылаются на None).У объекта list число ссылок снова уменьшается, и оно падает до нуля.На этом этапе объект list можно собирать мусором и освобождать память (когда это не гарантируется).

См. Также sys.getrefcount

1 голос
/ 19 июня 2019

В Python все переменные хранятся в словарях или структурах, которые очень похожи на словари (например, locals() может представлять текущую область / пространство имен в виде словаря).

Примечание : PyObject* - это концепция CPython. Я не уверен, как все работает в других реализациях Python.

Поэтому некорректно просматривать переменные Python, такие как C, где они имеют точные места в памяти. Их значения являются PyObject* (указателями или ячейками памяти), а не фактическими значениями примитивов. Поскольку сами переменные являются просто записями в словаре, которые указывают на PyObject* указатели, изменение значения переменной фактически дает ему другой адрес памяти, на который можно указать.

В CPython именно эти PyObject* значения используются id и is (a is b совпадает с id(a) == id(b).)

Например, давайте рассмотрим простую строку кода:

# x: int
x += 1

Фактически изменяет ячейку памяти, связанную с переменной. Это потому, что следует следующей логике:

LOAD_FAST (x)
LOAD_CONST (1)
INPLACE_ADD
STORE_FAST (x)

Какой байт-код примерно говорит:

  1. Поиск значения х. Это (в CPython) PyObject*, которое указывает на PyLongLong или около того (int из пользовательского пространства Python)

  2. Загрузить значение из постоянного адреса памяти

  3. Добавьте два значения. Это приведет к новому PyObject*, который также будет int
  4. Установить значение, связанное с x, как этот новый указатель

TL; DR : все, включая примитивы, в Python является объектом. Переменные хранят не сами значения, а указатели, которые их содержат. Переназначение переменной изменяет указатель, связанный с этим именем, а не обновляет память, хранящуюся в этом месте.

0 голосов
/ 25 июня 2019

Я объясняю вас на непрофессиональном языке, чтобы вы могли легко понять его.

Дело-1

a = [1, 2, 3]
b = a
print(b is a)

Значение a равно [1,2,3]. Теперь мы присваиваем [1,2,3] b также a. Таким образом, оба имеют одинаковое значение, поэтому b is a = True.

Следующий шаг,

a = {'x': 'y'}
print(a is b) 

Теперь вы меняете значение a на {'x':'y'} , но наш b по-прежнему такой же, как [1,2,3]. Так что теперь a is b это False.

Дело-2 Если вы сделали ниже: -

a = [1, 2, 3]
b = a
print(b is a)
a = {'x': 'y'}
b = a  # Reassigning the value of b.
print(a is b)

После переназначения значения a я также переназначаю значение b. Следовательно, вы получите True в обоих случаях.

Надеюсь, это поможет вам.

...