Почему (0-6) -6 = False?

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

Сегодня я попытался отладить мой проект, и после нескольких часов анализа я получил следующее:

>>> (0-6) is -6
False

но

>>> (0-5) is -5
True

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

> Python 2.7.3 (default, Apr 24 2012, 00:00:54) [GCC 4.7.0 20120414 (prerelease)] on linux2
>>> type(0-6) 
<type 'int'>
>>> type(-6) 
<type 'int'>
>>> type((0-6) is -6)
<type 'bool'>
>>> 

Ответ 1

Все целые числа от -5 до 256 включительно кэшируются в качестве глобальных объектов, разделяющих один и тот же адрес с CPython, таким образом, is тест проходит.

Этот артефакт подробно описан в http://www.laurentluce.com/posts/python-integer-objects-implementation/, и мы могли бы проверить текущий исходный код в http://hg.python.org/cpython/file./tip/Objects/longobject.c.

Специальная структура используется для обозначения маленьких целых чисел и совместного использования их, чтобы обеспечить быстрый доступ. Это массив из 262 указателей на целочисленные объекты. Эти целочисленные объекты размещаются во время инициализации в блоке целочисленных объектов, которые мы видели выше. Диапазон малых целых чисел - от -5 до 256. Многие программы на Python тратят много времени на использование целых чисел в этом диапазоне, поэтому это разумное решение.

Это только деталь реализации CPython, и вы не должны на это полагаться. Например, PyPy реализовал id целого числа для возврата самого себя, поэтому (0-6) is -6 всегда верно, даже если они внутренне являются "разными объектами"; это также позволяет вам настроить, включать ли это целочисленное кэширование, и даже устанавливать нижнюю и верхнюю границы. Но в целом объекты, полученные из разных источников, не будут идентичными. Если вы хотите сравнить равенство, просто используйте ==.

Ответ 2

Python хранит целые числа в диапазоне -5 - 256 в интерпретаторе: он имеет пул целых объектов, из которых возвращаются эти целые числа. Поэтому те объекты одинаковы: (0-5) и -5, но не (0-6) и -6, поскольку они создаются на месте.

Здесь источник в исходном коде CPython:

#define NSMALLPOSINTS           257
#define NSMALLNEGINTS           5
static PyIntObject *small_ints[NSMALLNEGINTS + NSMALLPOSINTS];

(просмотреть исходный код CPython: /trunk/Objects/intobject.c). Исходный код содержит следующий комментарий:

/* References to small integers are saved in this array so that they
   can be shared.
   The integers that are saved are those in the range
   -NSMALLNEGINTS (inclusive) to NSMALLPOSINTS (not inclusive).
*/

Затем оператор is сравнивает их (-5) как одинаковые, поскольку они являются одним и тем же объектом (такое же место памяти), но два других новых целых числа (-6) будут находиться в разных ячейках памяти (а затем is не вернется True). Обратите внимание, что 257 в приведенном выше исходном коде для положительных целых чисел, так что это 0 - 256 (включительно).

(источник)

Ответ 3

Это не ошибка. is не является критерием равенства. == даст ожидаемые результаты.

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

Ответ 4

Это происходит потому, что CPython кэширует небольшие целые числа и маленькие строки и дает каждому экземпляру этого объекта один и тот же id().

(0-5) и -5 имеет одинаковое значение для id(), что неверно для 0-6 и -6

>>> id((0-6))
12064324
>>> id((-6))
12064276
>>> id((0-5))
10022392
>>> id((-5))
10022392

Аналогично для строк:

>>> x = 'abc'
>>> y = 'abc'
>>> x is y
True
>>> x = 'a little big string'
>>> y = 'a little big string'
>>> x is y
False

Подробнее о строчном кэшировании читайте следующим образом: is оператор ведет себя по-разному при сравнении строк с пробелами