Особые случаи Java - несколько типов значений (включая целые числа), чтобы они сохранялись по значению (а не по ссылке на объект, как все остальное). В Python нет специальных случаев для таких типов, поэтому при назначении n множеству записей в списке (или другом обычном контейнере Python) не нужно делать копии.
Редактировать: обратите внимание, что ссылки всегда относятся к объектам , а не «к переменным» - в Python (или Java) не существует такого понятия, как «ссылка на переменную». Например:
>>> n = 23
>>> a = [n,n]
>>> print id(n), id(a[0]), id(a[1])
8402048 8402048 8402048
>>> n = 45
>>> print id(n), id(a[0]), id(a[1])
8401784 8402048 8402048
Мы видим из первого отпечатка, что обе записи в списке a
относятся к одному и тому же объекту, к которому относится n
- но когда n
переназначен, это теперь относится к другой объект, в то время как обе записи в a
все еще ссылаются на предыдущую.
array.array
(из модуля стандартной библиотеки Python массив ) очень отличается от списка: он хранит компактные копии однородного типа, занимая столько же битов на элемент, сколько необходимо для хранения копий значений этого типа. Все обычные контейнеры хранят ссылки (внутренне реализованные во время выполнения Python на C-языке как указатели на структуры PyObject: каждый указатель в 32-разрядной сборке занимает 4 байта, каждый PyObject не менее 16 или около того [включая указатель на тип, счетчик ссылок , фактическое значение и округление до значения malloc]), массивы - нет (поэтому они не могут быть неоднородными, не могут иметь элементы, кроме нескольких базовых типов и т. д.).
Например, контейнер из 1000 элементов, все элементы которого имеют разные маленькие целые числа (те, чьи значения могут помещаться в 2 байта каждый), будет принимать около 2000 байтов данных как array.array('h')
, но около 20000 как list
. Но если бы все элементы имели одно и то же число, массив по-прежнему занимал бы 2000 байт данных, список занимал бы всего 20 или около того [[в каждом из этих случаев вы должны добавить еще около 16 или 32 байта для объекта-контейнера собственно, в дополнение к памяти для данных]].
Однако, хотя вопрос говорит о «массиве» (даже в теге), я сомневаюсь, что его arr
на самом деле является массивом - если бы он не был сохранен (2 ** 32) * 2 (наибольший int значения в массиве - 32 бита), и поведение памяти, о котором сообщается в вопросе, фактически не будет наблюдаться. Таким образом, вопрос, скорее всего, касается списка, а не массива.
Редактировать : комментарий @ooboo задает множество разумных последующих вопросов, и вместо того, чтобы пытаться сжать подробное объяснение в комментарии, я перемещаю его сюда.
Хотя это странно - в конце концов, как
ссылка на целое число сохраняется?
id (переменная) дает целое число,
ссылка сама по себе целое число, не
дешевле использовать целое число?
CPython хранит ссылки в виде указателей на PyObject (Jython и IronPython, написанные на Java и C #, используют неявные ссылки этих языков; PyPy, написанный на Python, имеет очень гибкий бэкэнд и может использовать множество различных стратегий)
id(v)
дает (только на CPython) числовое значение указателя (просто как удобный способ уникальной идентификации объекта). Список может быть разнородным (некоторые элементы могут быть целыми числами, другие - объектами разных типов), поэтому не имеет смысла сохранять некоторые элементы как указатели на PyObject и другие по-разному (каждый объект также нуждается в указании типа и, в CPython, количество ссылок, по крайней мере) - array.array
является однородным и ограниченным, так что он может (и действительно) хранить копию значений элементов, а не ссылок (это часто дешевле, но не для коллекций, где тот же элемент появляется LOT, например, разреженный массив, в котором подавляющее большинство элементов равно 0).
Реализация Python будет полностью разрешена языковыми спецификациями для того, чтобы попробовать более тонкие трюки для оптимизации, при условии, что она не затрагивает семантику, но, насколько я знаю, в настоящее время никто не делает для этой конкретной проблемы (вы можете попробовать взломать бэкэнд PyPy). , но не удивляйтесь, если накладные расходы на проверку int и non-int сокрушат ожидаемый выигрыш).
Кроме того, будет ли это иметь значение, если я
вместо этого назначается 2 64 каждому слоту
присваивания n, когда n содержит
ссылка на 2 64? Что происходит, когда
Я просто пишу 1?
Это примеры выбора реализации, который может быть разрешен для каждой реализации, поскольку нетрудно сохранить семантику (поэтому даже гипотетически даже, скажем, 3.1 и 3.2 могут вести себя по-разному в этом отношении).
Когда вы используете литерал int (или любой другой литерал неизменяемого типа) или другое выражение, создающее результат такого типа, то реализация должна решить, создавать ли новый объект этого типа безоговорочно, или потратьте некоторое время на проверку среди таких объектов, чтобы выяснить, существует ли существующий объект, который он может использовать повторно.
На практике, CPython (и я полагаю, что другие реализации, но я менее знаком с их внутренними компонентами) использует одну копию достаточно маленьких целых чисел (сохраняет предопределенный массив C из нескольких маленьких целых чисел). значения в форме PyObject, готовые к использованию или повторному использованию при необходимости), но в целом они не пытаются найти другие существующие объекты многократного использования.
Но, например, идентичные литеральные константы в одной и той же функции легко и легко компилируются как ссылки на один константный объект в таблице констант функции, так что это оптимизация, которую очень легко выполнить, и я считаю, что каждая текущая реализация Python выполняет это.
Иногда бывает трудно вспомнить, чем Python является языком , и у него есть несколько реализаций, которые могут (законно и правильно) различаться во многих таких деталях - все, включая педантов вроде меня, склонны говорить просто «Python», а не «CPython», когда речь идет о популярной C-кодированной реализации (за исключением случаев, подобных этому, где первостепенное значение имеет различие между языком и реализацией ;-). Тем не менее, различие является весьма важным, и его стоит повторить время от времени.