Почему (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
оператор ведет себя по-разному при сравнении строк с пробелами