Что происходит, когда вы назначаете значение одной переменной другой переменной в Python?

Это мой второй день изучения python (я знаю основы С++ и некоторые OOP.), и у меня есть небольшая путаница в отношении переменных в python.

Вот как я их понимаю в настоящее время:

Переменные Python являются ссылками (или указателями?) на объекты (которые являются изменяемыми или неизменяемыми). Когда у нас есть что-то вроде num = 5, неизменный объект 5 создается где-то в памяти, а пара ссылок <имя-объектa > объекта-объекта-name-object создается в определенном пространстве имен. Когда мы имеем a = num, ничего не копируется, но теперь обе переменные относятся к одному и тому же объекту, а a добавляется в одно и то же пространство имен.

Здесь меня смущает моя книга "Автоматизация скучных вещей с Python". Поскольку это книга новичков, в ней не упоминаются объекты, пространства имен и т.д., И он пытается объяснить следующий код:

>>> spam = 42
>>> cheese = spam
>>> spam = 100
>>> spam
100
>>> cheese
42

Объяснение, которое он предлагает, точно такое же, как и в книге на С++, что мне не нравится, поскольку мы имеем дело со ссылками/указателями на объекты. Таким образом, в этом случае я полагаю, что в 3-й строке, поскольку целые числа неизменны, spam получает совершенно новый указатель/ссылку на другое место в памяти, то есть память, на которую она первоначально указывала, не была изменена, Следовательно, мы имеем cheese, ссылаясь на начальный объект, на который ссылается spam. Это правильное объяснение?

Ответ 1

Как разработчик С++ вы можете думать о переменных Python как указатели.

Таким образом, когда вы пишете spam = 100, это означает, что вы "назначаете указатель", который ранее указывал на объект 42, чтобы указать на объект 100.

Ранее, cheese было назначено указать на тот же объект, что и указатель spam, который в то время был 42. Поскольку вы не модифицировали cheese, он все еще указывает на 42.

В этом случае неизменяемость не имеет к этому отношения, поскольку назначение указателя ничего не меняет в отношении объекта, на который указывает.

Ответ 2

То, как я вижу это, есть разные взгляды на язык.

  • "Адвокат языка".
  • Перспектива "практического программиста".
  • перспектива "реализатора".

С точки зрения адвоката языка переменные python всегда "указывают на" объект. Однако, в отличие от Java и С++, поведение == <= >= etc зависит от типа среды выполнения объектов, на которые указывают переменные. Кроме того, в python память управляется языком.

С точки зрения практического программиста мы можем рассматривать тот факт, что целые числа, строки, кортежи и т.д. являются неизменяемыми * объектами, а не прямыми значениями как нерелевантные детали. Исключение составляет хранение больших массивов числовых данных, которые мы можем использовать для типов, которые могут хранить значения напрямую (например, массивы numpy), а не типы, которые будут содержать массив, полный ссылок на крошечные объекты.

С точки зрения разработчиков большинство языков имеют какое-то правило as-if, так что если указанные поведения верны, реализация правильная, независимо от того, как вещи действительно выполняются под капотом.

Итак, да, ваше объяснение верно с точки зрения адвоката языка. Ваша книга верна с точки зрения практического программиста. Фактическая реализация на самом деле зависит от реализации. В целых числах cpython являются реальными объектами, хотя небольшие значения целых чисел берутся из пула кеша, а не создаются заново. Я не уверен, что делают другие реализации (например, pypy и jython).

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

Ответ 3

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

Сначала мы будем в значительной степени использовать функцию id:

Верните "идентификатор" объекта. Это целое число, которое гарантировано будет уникальным и постоянным для этого объекта в течение его жизни. Два объекта с неперекрывающимся временем жизни могут иметь одинаковое значение id().

Вероятно, это вернет разные абсолютные значения на вашем компьютере.

Рассмотрим следующий пример:

>>> foo = 'a string'
>>> id(foo) 
4565302640
>>> bar = 'a different string'
>>> id(bar)
4565321816
>>> bar = foo
>>> id(bar) == id(foo)
True
>>> id(bar)
4565302640

Вы можете видеть, что:

  • В исходном foo/bar есть разные идентификаторы, поскольку они указывают на разные объекты.
  • Когда bar назначается foo, их идентификаторы теперь одинаковы. Это похоже на то, что они указывают на то же место в памяти, которое вы видите при создании указателя на С++

когда мы меняем значение foo, ему присваивается другой id:

>>> foo = 42
>>> id(foo)
4561661488
>>> foo = 'oh no'
>>> id(foo)
4565257832
Интересно также отметить, что целые числа неявно имеют эту функциональность до 256:
>>> a = 100
>>> b = 100
>>> c = 100
>>> id(a) == id(b) == id(c)
True

Однако после 256 это уже не так:

>>> a = 256
>>> b = 256
>>> id(a) == id(b)
True
>>> a = 257
>>> b = 257
>>> id(a) == id(b)
False

однако присвоение a до b действительно сохранит идентификатор так же, как показано выше:

>>> a = b
>>> id(a) == id(b)
True

Ответ 4

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

Подумайте об этом как "pass-by-alias", если вам нужен тот же тип фразы или, возможно, "pass-by-object", потому что вы можете мутировать один и тот же объект из любой переменной, которая указывает на него, если она изменена, но переназначение переменной (псевдоним) изменяет только одну переменную.

Если это помогает: переменные C представляют собой поля, в которые вы записываете значения. Имена Python - это теги, которые вы добавляете в значения.

Имя переменной Python является ключом в глобальном (или локальном) пространстве имен, которое фактически является словарем. Базовое значение - это некоторый объект в памяти. Назначение дает имя этому объекту. Присвоение одной переменной другой переменной означает, что обе переменные являются именами для одного и того же объекта. Повторное назначение одной переменной изменяет объект, который указан этой переменной, без изменения другой переменной. Вы переместили тег, но не изменили предыдущий объект или любые другие теги на нем.

В базовом C-коде реализации CPython каждый объект Python является PyObject*, поэтому вы можете думать о нем как о работе как C, если у вас только когда-либо были указатели на данные (без указателей на указатели, нет напрямую -ограниченные значения).

вы могли бы сказать, что Python является pass-by-value, где значения являются указателями... или вы могли бы сказать, что Python является передачей по ссылке, где ссылки являются копиями.

Ответ 5

При запуске spam = 100 python создайте еще один объект в памяти, но не измените существующий. поэтому у вас все еще есть указатель cheese до 42 и spam до 100

Ответ 6

Что происходит в строке spam = 100 - это замена предыдущего значения (указатель на объект типа int со значением 42) с другим указателем на другой объект (тип int, значение 100)

Ответ 7

Как упоминается в комментариях @DeepSpace, Ned Batchelder отлично справляется с демистификацией переменных (имен) и назначений значений в блоге, из которых он выступил с докладом на PyCon 2015, Факты и мифы о именах и значениях Python. Это может быть проницательным для Pythonistas на любом уровне мастерства.

Ответ 8

Когда вы храните spam = 42, он создает объект в памяти. Затем вы назначаете cheese = spam, он присваивает объект, на который ссылается spam, на cheese. И, наконец, при изменении spam = 100 он меняет только объект spam. Итак, cheese = 42.