Идентичность Python: множественное расстройство личности, необходимость в сокращении кода

Возможный дубликат:
Оператор Python "is" неожиданно работает с целыми числами

Я наткнулся на следующую Python странность:

>>> two = 2
>>> ii = 2

>>> id(two) == id(ii)
True
>>> [id(i) for i in [42,42,42,42]]
[10084276, 10084276, 10084276, 10084276]

>>> help(id)
Help on built-in function id in module __builtin__:

id(...)
    id(object) -> integer

    Return the identity of an object.  This is guaranteed to be unique among
    simultaneously existing objects.  (Hint: it the object memory address.)
  • Является ли каждое число уникальным объектом?
  • Существуют ли разные переменные, содержащие одни и те же элементарные значения (например, два, ii) один и тот же объект?
  • Как идентификатор числа, сгенерированного Python?
  • В приведенном выше примере представлены два и 2 указателя на ячейку памяти, содержащую значение 2? Это было бы чрезвычайно странно.

Помогите мне распутать этот кризис идентичности.

Еще несколько странностей:

>>> a,b=id(0),id(1)
>>> for i in range(2,1000):
   a,b=b,id(i)
   if abs(a-b) != 12:
    print('%i:%i -> %i' % (i,a,b))

В приведенном выше коде рассматривается, если иды последовательных целых чисел также последовательны и распечатываются аномалии:

77:10083868 -> 10085840
159:10084868 -> 10086840
241:10085868 -> 10087840
257:10087660 -> 11689620
258:11689620 -> 11689512
259:11689512 -> 11689692
260:11689692 -> 11689548
261:11689548 -> 11689644
262:11689644 -> 11689572
263:11689572 -> 11689536
264:11689536 -> 11689560
265:11689560 -> 11689596
266:11689596 -> 11689656
267:11689656 -> 11689608
268:11689608 -> 11689500
331:11688756 -> 13807288
413:13806316 -> 13814224
495:13813252 -> 13815224
577:13814252 -> 13816224
659:13815252 -> 13817224
741:13816252 -> 13818224
823:13817252 -> 13819224
905:13818252 -> 13820224
987:13819252 -> 13821224

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

Ответ 1

Ваш четвертый вопрос: "в приведенном выше примере два указателя и ii указатели на ячейку памяти, содержащую значение 2?, которое было бы чрезвычайно странным", действительно является ключом к пониманию всего этого.

Если вы знакомы с такими языками, как C, "переменные" Python не работают одинаково. Объявление переменной C как:

int j=1;
int k=2;
k += j;

говорит: "компилятор, зарезервируйте для меня две области памяти в стеке, каждый с достаточным пространством для хранения целого числа, и запомните один как" j ", а другой как" k ". Затем заполните j значением '1' и k со значением '2'." Во время выполнения код говорит: "возьмите целочисленное содержимое k, добавьте целочисленное содержимое j и верните результат в k."

По-видимому, эквивалентный код в Python:

j = 1
k = 2
k += j

говорит что-то другое: "Python, найдите объект, известный как" 1 ", и создайте метку с именем" j ", которая указывает на нее. Посмотрите на объект, известный как" 2 ", и создайте ярлык" k " ', который указывает на это. Теперь найдите объект' k 'указывает на (' 2 '), найдите объект' j 'points to (' 1 ') и наведите' k 'на объект, полученный в результате выполнения' добавьте 'операцию на два.

Разборка этого кода (с помощью этого модуля) показывает это красиво:

  2           0 LOAD_CONST               1 (1)
              3 STORE_FAST               0 (j)

  3           6 LOAD_CONST               1 (2)
              9 STORE_FAST               1 (k)

  4          12 LOAD_FAST                1 (k)
             15 LOAD_FAST                0 (j)
             18 INPLACE_ADD
             19 STORE_FAST               1 (k)

Итак, да, "переменные" Python - это метки, указывающие на объекты, а не на контейнеры, которые могут быть заполнены данными.

Остальные три вопроса - все варианты "когда Python создает новый объект из куска кода и когда он повторно использует тот, который уже есть?". Последний называется "интернированием"; это случается с меньшими целыми числами и строками, которые выглядят (на Python), как они могут быть именами символов.

Ответ 2

Целые числа от -1 до 255 (?), а также строковые литералы интернированы. Каждый экземпляр в источнике фактически представляет один и тот же объект.

В CPython результатом id() является адрес в пространстве процесса PyObject.

Ответ 3

Каждая реализация Python полностью разрешена в любой степени (в том числе... вообще отсутствует;-) идентификация и выделение объектов неизменяемых (таких как числа, кортежи и строки) [[нет такой широты для изменяемых объектов, таких как списки, dicts и sets]].

Между двумя неизменяемыми объектными ссылками a и b, вся реализация должна гарантировать:

  • id(a) == id(b), AKA a is b, всегда должен подразумевать a == b
  • и поэтому a != b всегда должен подразумевать id(a) != id(b) AKA a is not b

Обратите внимание, в частности, существует ограничение нет, даже для неизменяемых типов, что a == b должно означать a is b (т.е. id(a) == id(b)). Только None делает эту гарантию (поэтому вы всегда можете проверить if x is None:, а не if x == None:).

Существующие реализации CPython используют эти степени свободы путем "слияния" (имея одно выделение, таким образом, одиночные id, для) малые целые числа в определенном диапазоне и встроенные объекты неизменяемого типа, чьи литералы появляются (например, если ваша функция f имеет четыре вхождения буквального 'foobar', все они будут ссылаться на один экземпляр строки 'foobar' внутри констант функции, сохраняя небольшое пространство по сравнению с допустимая реализация, которая хранит четыре идентичные, но отдельные копии этой константы).

Все эти соображения по реализации довольно незначительны для кодов Python (если вы не работаете над реализацией Python или, по крайней мере, с тем, что тесно связано с конкретной реализацией, например с системой отладки).

Ответ 4

Вы должны быть очень осторожны с этими исследованиями. Вы изучаете внутренние аспекты реализации языка, и это не гарантируется. Справка по id является точечной: число будет отличаться для двух разных объектов и одинаково для одного и того же объекта. В качестве детали реализации, в CPython это адрес памяти объекта. CPython может решить изменить эту деталь в любое время.

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

Кроме того, если вы переключаетесь с CPython на Jython или PyPy или IronPython, все ставки отключены, кроме документации на id().

Ответ 5

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