6

Оператор "is" ведет себя неожиданно с целыми числами

14

Почему следующий код ведет себя неожиданно в Python?

>>> a = 256
>>> b = 256
>>> a is b
True           # Это ожидаемый результат
>>> a = 257
>>> b = 257
>>> a is b
False          # Что здесь произошло? Почему это False?
>>> 257 is 257
True           # Тем не менее, литеральные числа сравниваются корректно

Я использую Python 2.5.2. Проверив другие версии Python, я заметил, что в Python 2.3.3 наблюдается аналогичное поведение для чисел от 99 до 100.

Исходя из вышеизложенного, я могу предположить, что Python реализует "маленькие" целые числа и "большие" целые числа по-разному, и оператор is может это различать. Почему такая "протечка абстракции"? Какой более подходящий способ сравнения двух произвольных объектов, чтобы узнать, являются ли они одинаковыми, если я заранее не знаю, числа они или нет?

4 ответ(ов)

0

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

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

In [29]: a = 3
In [30]: b = 3
In [31]: id(a)
Out[31]: 500729144
In [32]: id(b)
Out[32]: 500729144

Для сравнения равенства произвольных объектов следует использовать ==. Поведение этого сравнения можно задать с помощью атрибутов __eq__ и __ne__.

0

Вы правы, ваша гипотеза кажется верной. В Python числа в диапазоне от -5 до 256 (включительно) кэшируются для оптимизации, что и объясняет одинаковые значения идентификаторов для объектов с числами 255.

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

In [1]: id(255)
Out[1]: 146349024

In [2]: id(255)
Out[2]: 146349024  # Тот же идентификатор, так как 255 кэшируется.

In [3]: id(257)
Out[3]: 146802752

In [4]: id(257)
Out[4]: 148993740  # Разные идентификаторы, так как 257 не кэшируется.

In [5]: a = 255
In [6]: b = 255
In [7]: c = 257
In [8]: d = 257
In [9]: id(a), id(b), id(c), id(d)
Out[9]: (146349024, 146349024, 146783024, 146804020)

Как видно из результатов, a и b, оба равные 255, имеют один и тот же идентификатор, в то время как c и d, которые равны 257, имеют разные идентификаторы. Это подтверждает, что числа <= 255 обрабатываются как литералы (или кэшируются), в то время как числа больше этого значения создаются как новые объекты.

0

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

0

Такое поведение связано с особенностями управления памятью в Python, особенно для строк. Изначально Python использует оптимизацию для малых и общепринятых строк: когда вы создаете строку, которая совпадает с уже существующей (например, строки короткие и часто используемые), интерпретатор может использовать одну и ту же область памяти, и соответственно, is будет возвращать True, так как обе переменные ссылаются на один и тот же объект.

Теперь давайте разберемся с вашим примером:

  1. В первом случае, когда вы присваиваете s = b = 'somestr', s и b указывают на одну и ту же строку в памяти. Поэтому как s == b, так и s is b вернут True.

  2. Во втором случае с s и b, оба опять указывают на одну и ту же строку, следовательно, результат такой же: True.

  3. Однако в третьем примере, когда строки длиннее или менее характерные, Python может создать новые объекты в памяти для каждой строки при их инициализации. Когда вы присваиваете s1 и b1 разные значения, даже если они содержат одинаковый текст, интерпретатор может выделить разные области памяти для них. Это приводит к тому, что s1 is b1 возвращает False, несмотря на то что s1 == b1 возвращает True, потому что содержимое строк совпадает, а ссылки на объекты различны.

Итак, если вы хотите проверить, указывают ли две переменные на один и тот же объект, используйте is. Если вам нужно проверить равенство содержимого, используйте ==.

Чтобы ответить на вопрос, пожалуйста, войдите или зарегистрируйтесь