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

509

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

>>> a = 256
>>> b = 256
>>> a is b
True           # This is an expected result
>>> a = 257
>>> b = 257
>>> a is b
False          # What happened here? Why is this False?
>>> 257 is 257
True           # Yet the literal numbers compare properly

Я использую Python 2.5.2. При попытке использовать несколько разных версий Python, Python 2.3.3 демонстрирует вышеуказанное поведение между 99 и 100.

Исходя из вышеизложенного, я могу предположить, что Python реализован внутренне так, что «маленькие» целые числа хранятся не так, как большие целые числа, и isоператор может отличить их. Почему дырявая абстракция? Что может быть лучше для сравнения двух произвольных объектов, чтобы увидеть, являются ли они одинаковыми, когда я не знаю заранее, являются ли они числами или нет?

Грег Хьюгилл
источник
1
Посмотрите здесь > Текущая реализация хранит массив целочисленных объектов для всех> целых чисел в диапазоне от -5 до 256, когда вы создаете int в этом диапазоне, вы фактически просто получаете ссылку на существующий объект.
user5319825
2
Это специфичная для CPython реализация и неопределенное поведение, используйте с
осторожностью

Ответы:

393

Взгляните на это:

>>> a = 256
>>> b = 256
>>> id(a)
9987148
>>> id(b)
9987148
>>> a = 257
>>> b = 257
>>> id(a)
11662816
>>> id(b)
11662828

Вот что я нашел в документации по Python 2 «Простые целочисленные объекты» (то же самое для Python 3 ):

Текущая реализация хранит массив целочисленных объектов для всех целых чисел от -5 до 256, когда вы создаете int в этом диапазоне, вы на самом деле просто получаете ссылку на существующий объект. Так что должно быть возможно изменить значение 1. Я подозреваю, что поведение Python в этом случае не определено. :-)

Cybis
источник
46
Кто-нибудь знает, как был выбран этот диапазон (-5, 256)? я не был бы слишком удивлен, если бы это было (0, 255) или даже (-255, 255), но диапазон из 262 чисел, начинающихся с -5, кажется удивительно произвольным.
Вудроу Барлоу
6
@WoodrowBarlow: Я думаю, что -5 - это просто эвристика для захвата общих отрицательных заполнителей. 0..255 покрывает массивы однобайтовых значений. Это 256, что загадочно, но я думаю, что это для (раз) сборки целых чисел в / из байтов.
Дэвис Херринг
3
Из того, что я понимаю, диапазон был выбран путем рассмотрения часто используемых значений в нескольких проектах (и на нескольких языках).
Тони Саффолк 66
9
Согласно reddit.com/r/Python/comments/18leav/… , диапазон был [-5,100]. Он был расширен, чтобы включить полный диапазон значений байтов - плюс 256, потому что это, вероятно, общее число.
mwfearnley
2
@Ashwani попробуйте прочитать комментарии рядом с вашим комментарием, опубликованным за два года до вашего, и вы найдете ответ на свой вопрос.
JBG
116

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

В заключение - позвольте мне подчеркнуть: не используйте isдля сравнения целых чисел.

Это не то поведение, о котором вы должны ожидать.

Вместо этого используйте ==и !=для сравнения на равенство и неравенство соответственно. Например:

>>> a = 1000
>>> a == 1000       # Test integers like this,
True
>>> a != 5000       # or this!
True
>>> a is 1000       # Don't do this! - Don't use `is` to test integers!!
False

объяснение

Чтобы знать это, вам нужно знать следующее.

Во-первых, что делает is? Это оператор сравнения. Из документации :

Операторы isи is notпроверка на идентичность объекта: x is yистинно тогда и только тогда, когда x и y являются одним и тем же объектом. x is not yдает обратное значение истины.

И поэтому следующие эквивалентны.

>>> a is b
>>> id(a) == id(b)

Из документации :

id Вернуть «личность» объекта. Это целое число (или длинное целое), которое гарантированно будет уникальным и постоянным для этого объекта в течение его жизни. Два объекта с неперекрывающимися временами жизни могут иметь одинаковое id()значение.

Обратите внимание, что тот факт, что id объекта в CPython (эталонная реализация Python) является местоположением в памяти, является деталью реализации. Другие реализации Python (такие как Jython или IronPython) могут легко иметь другую реализацию для id.

Так для чего нужен вариант is? PEP8 описывает :

Сравнения с синглетонами, такими как Noneвсегда, должны выполняться с isили is notникогда, а не с операторами равенства.

Вопрос

Вы задаете и задаете следующий вопрос (с кодом):

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

>>> a = 256
>>> b = 256
>>> a is b
True           # This is an expected result

Это не ожидаемый результат. Почему это ожидается? Это только означает, что целые числа, на которые 256ссылаются оба, aи bявляются одним и тем же экземпляром целого числа. Целые числа неизменны в Python, поэтому они не могут измениться. Это не должно иметь никакого влияния на любой код. Этого не следует ожидать. Это просто деталь реализации.

Но, возможно, нам следует порадоваться, что в памяти нет нового отдельного экземпляра каждый раз, когда мы указываем значение, равное 256.

>>> a = 257
>>> b = 257
>>> a is b
False          # What happened here? Why is this False?

Похоже, теперь у нас есть два отдельных экземпляра целых чисел со значением 257в памяти. Так как целые числа неизменны, это тратит впустую память. Будем надеяться, что мы не будем тратить на это много. Мы, вероятно, нет. Но такое поведение не гарантируется.

>>> 257 is 257
True           # Yet the literal numbers compare properly

Что ж, похоже, ваша конкретная реализация Python пытается проявить смекалку и не создает избыточные целые числа в памяти без необходимости. Похоже, вы указываете, что используете референтную реализацию Python, то есть CPython. Хорошо для CPython.

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

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

Что isделает

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

>>> a is b

такой же как

>>> id(a) == id(b)

Почему мы хотим использовать isтогда?

Это может быть очень быстрая проверка, скажем, проверка, равны ли две очень длинные строки по значению. Но так как это относится к уникальности объекта, у нас, таким образом, есть ограниченные варианты использования для него. Фактически, мы в основном хотим использовать его для проверки None, который является единственным (единственный экземпляр, существующий в одном месте в памяти). Мы можем создать другие синглтоны, если есть возможность их сопоставить, с которыми мы могли бы проверить is, но они относительно редки. Вот пример (будет работать в Python 2 и 3), например:

SENTINEL_SINGLETON = object() # this will only be created one time.

def foo(keyword_argument=None):
    if keyword_argument is None:
        print('no argument given to foo')
    bar()
    bar(keyword_argument)
    bar('baz')

def bar(keyword_argument=SENTINEL_SINGLETON):
    # SENTINEL_SINGLETON tells us if we were not passed anything
    # as None is a legitimate potential argument we could get.
    if keyword_argument is SENTINEL_SINGLETON:
        print('no argument given to bar')
    else:
        print('argument to bar: {0}'.format(keyword_argument))

foo()

Какие отпечатки:

no argument given to foo
no argument given to bar
argument to bar: None
argument to bar: baz

Итак, мы видим, с помощью isи дозорного, мы можем различать, когда barвызывается без аргументов и когда вызывается с None. Это основные варианты использования для is- не используйте его для проверки на равенство целых чисел, строк, кортежей или других подобных вещей.

Аарон Холл
источник
«Это основные варианты использования для is- не используйте его для проверки на равенство целых чисел, строк, кортежей или других подобных вещей». Тем не менее, я пытаюсь интегрировать простой конечный автомат в мой класс, и, поскольку состояния являются непрозрачными значениями, единственное наблюдаемое свойство которых состоит в том, что они идентичны или различны, для них вполне естественно быть сопоставимыми is. Я планирую использовать интернированные строки в качестве состояний. Я бы предпочел простые целые числа, но, к сожалению, Python не может интернировать целые числа ( 0 is 0это детали реализации).
Алексей
@ Алексей звучит так, как будто тебе нужны перечисления? stackoverflow.com/questions/37601644/…
Аарон Холл
Может быть, спасибо, не знал о них. Это может быть подходящим дополнением к вашему ответу IMO.
Алексей
Возможно, использование нескольких глупых объектов, таких как страж, в вашем ответе было бы более легким решением ...
Алексей,
Перечисления @Alexey находятся в стандартной библиотеке Python 3, и это, вероятно, поощрило бы ваш код быть немного более значимым, чем простые стражи.
Аарон Холл
60

Это зависит от того, хотите ли вы увидеть, равны ли 2 вещи или один и тот же объект.

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

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

Вы должны использовать, ==чтобы сравнить равенство произвольных объектов. Вы можете указать поведение с помощью атрибутов __eq__, and __ne__.

JimB
источник
Большие пальцы за фактическое объяснение, как сравнивать произвольные объекты, как ОП спросил !!
Joooeey
54

Я опоздал, но вы хотите какой-нибудь источник с вашим ответом? Я попытаюсь сказать это во вступительной манере, чтобы больше людей могли следовать за ними.


Хорошая вещь о CPython в том, что вы можете увидеть источник этого. Я собираюсь использовать ссылки для версии 3.5 , но найти соответствующие 2.x тривиально.

В CPython функция C-API, которая обрабатывает создание нового intобъекта PyLong_FromLong(long v). Описание этой функции:

Текущая реализация хранит массив целочисленных объектов для всех целых чисел от -5 до 256, когда вы создаете int в этом диапазоне, вы на самом деле просто получаете ссылку на существующий объект . Так что должно быть возможно изменить значение 1. Я подозреваю, что поведение Python в этом случае не определено. :-)

(Мой курсив)

Не знаю как вы, но я вижу это и думаю: давайте найдем этот массив!

Если вы не возились с кодом C, реализующим CPython, вам следует ; все довольно организовано и читабельно. Для нашего случая, мы должны смотреть в Objectsподкаталоге из основного исходного кода дерева каталогов .

PyLong_FromLongимеет дело с longобъектами, поэтому нетрудно понять, что нам нужно заглянуть внутрь longobject.c. Заглянув внутрь, вы можете подумать, что все хаотично; они, но не бойтесь, функция, которую мы ищем, охлаждает линию 230, ожидая, чтобы мы ее проверили. Это небольшая функция, поэтому основная часть (исключая объявления) легко вставляется сюда:

PyObject *
PyLong_FromLong(long ival)
{
    // omitting declarations

    CHECK_SMALL_INT(ival);

    if (ival < 0) {
        /* negate: cant write this as abs_ival = -ival since that
           invokes undefined behaviour when ival is LONG_MIN */
        abs_ival = 0U-(unsigned long)ival;
        sign = -1;
    }
    else {
        abs_ival = (unsigned long)ival;
    }

    /* Fast path for single-digit ints */
    if (!(abs_ival >> PyLong_SHIFT)) {
        v = _PyLong_New(1);
        if (v) {
            Py_SIZE(v) = sign;
            v->ob_digit[0] = Py_SAFE_DOWNCAST(
                abs_ival, unsigned long, digit);
        }
        return (PyObject*)v; 
}

Так вот, мы не мастер-код С-haxxorz, но мы также не глупы , мы можем видеть, что CHECK_SMALL_INT(ival);подглядывает на нас всех соблазнительно; мы можем понять, что это как-то связано с этим. Давайте проверим это:

#define CHECK_SMALL_INT(ival) \
    do if (-NSMALLNEGINTS <= ival && ival < NSMALLPOSINTS) { \
        return get_small_int((sdigit)ival); \
    } while(0)

Так что это макрос, который вызывает функцию, get_small_intесли значение ivalудовлетворяет условию:

if (-NSMALLNEGINTS <= ival && ival < NSMALLPOSINTS)

Так что же NSMALLNEGINTSи NSMALLPOSINTS? Макросы! Вот они :

#ifndef NSMALLPOSINTS
#define NSMALLPOSINTS           257
#endif
#ifndef NSMALLNEGINTS
#define NSMALLNEGINTS           5
#endif

Итак, наше условие это if (-5 <= ival && ival < 257)вызовget_small_int .

Далее давайте рассмотрим get_small_intво всей красе (ну, мы просто посмотрим на его тело, потому что именно там есть интересные вещи):

PyObject *v;
assert(-NSMALLNEGINTS <= ival && ival < NSMALLPOSINTS);
v = (PyObject *)&small_ints[ival + NSMALLNEGINTS];
Py_INCREF(v);

Хорошо, объявляем a PyObject, утверждаем, что предыдущее условие выполняется, и выполняем присваивание:

v = (PyObject *)&small_ints[ival + NSMALLNEGINTS];

small_intsвыглядит очень похоже на тот массив, который мы искали, и это так! Мы могли бы просто прочитать эту чертову документацию, и мы бы все знали! :

/* Small integers are preallocated in this array so that they
   can be shared.
   The integers that are preallocated are those in the range
   -NSMALLNEGINTS (inclusive) to NSMALLPOSINTS (not inclusive).
*/
static PyLongObject small_ints[NSMALLNEGINTS + NSMALLPOSINTS];

Итак, это наш парень. Когда вы захотите создать новый intв диапазоне, [NSMALLNEGINTS, NSMALLPOSINTS)вы просто получите ссылку на уже существующий объект, который был предварительно выделен.

Поскольку ссылка ссылается на один и тот же объект, id()прямая выдача или проверка идентичности с isним вернет точно то же самое.

Но когда они распределяются ??

Во время инициализации в_PyLong_Init Python с удовольствием войдем в цикл for, сделайте это за вас:

for (ival = -NSMALLNEGINTS; ival <  NSMALLPOSINTS; ival++, v++) {

Проверьте источник, чтобы прочитать тело цикла!

Я надеюсь, что мое объяснение сделало вас ясными с вещами теперь (игра слов явно была намерена).


Но 257 is 257? Что происходит?

Это на самом деле легче объяснить, и я уже пытался это сделать ; это связано с тем, что Python выполнит этот интерактивный оператор как один блок:

>>> 257 is 257

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

>>> codeObj = compile("257 is 257", "blah!", "exec")
>>> codeObj.co_consts
(257, None)

Когда CPython выполняет операцию, он просто собирается загрузить точно такой же объект:

>>> import dis
>>> dis.dis(codeObj)
  1           0 LOAD_CONST               0 (257)   # dis
              3 LOAD_CONST               0 (257)   # dis again
              6 COMPARE_OP               8 (is)

Так isвернется True.

Димитрис Фасаракис Хиллиард
источник
37

Как вы можете проверить в исходном файле intobject.c , Python для эффективности кэширует маленькие целые числа. Каждый раз, когда вы создаете ссылку на маленькое целое число, вы ссылаетесь на кешированное маленькое целое число, а не на новый объект. 257 - не маленькое целое число, поэтому оно рассчитывается как другой объект.

Лучше использовать ==для этой цели.

ангел
источник
19

Я думаю, что ваши гипотезы верны. Эксперимент с id(личность объекта):

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

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

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

In [4]: id(257)
Out[4]: 148993740

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)

Похоже, что числа <= 255трактуются как литералы, а все вышеперечисленное трактуется иначе!

Amit
источник
1
Это связано с тем, что объекты, представляющие значения от -5 до +256, создаются во время запуска, и поэтому все эти значения используются для предварительной сборки объекта. Почти все ссылки на целые числа вне этого диапазона создают новый внутренний объект каждый раз, когда на них ссылаются. Я думаю, что использование термина литерал сбивает с толку - литерал обычно относится к любому значению, которое вводится в куске кода - поэтому все числа в исходном коде являются литералами.
Тони Саффолк 66
13

Для объектов с неизменяемыми значениями, таких как int, строки или datetime, идентичность объекта не особенно полезна. Лучше подумать о равенстве. Идентичность - это, по сути, деталь реализации для объектов-значений - поскольку они неизменны, нет эффективной разницы между наличием нескольких ссылок на один и тот же объект или несколько объектов.

babbageclunk
источник
12

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


С одной стороны, есть некоторые другие предварительно созданные значения, такие как пустые tuple, strи bytes, и некоторые короткие строки (в CPython 3.6 это 256 односимвольных строк Latin-1). Например:

>>> a = ()
>>> b = ()
>>> a is b
True

Но даже и не созданные ранее значения могут быть идентичными. Рассмотрим эти примеры:

>>> c = 257
>>> d = 257
>>> c is d
False
>>> e, f = 258, 258
>>> e is f
True

И это не ограничивается intценностями:

>>> g, h = 42.23e100, 42.23e100
>>> g is h
True

Очевидно, что CPython не поставляется с предварительно созданным floatзначением для42.23e100 . Итак, что здесь происходит?

Компилятор CPython сольются постоянные значения некоторых заведомо неизменных типов , таких как int, float, str, bytes, в том же модуле компиляции. Для модуля весь модуль является модулем компиляции, но в интерактивном интерпретаторе каждый оператор является отдельным модулем компиляции. Поскольку cи dопределены в отдельных операторах, их значения не объединяются. Поскольку eи fопределены в одном и том же утверждении, их значения объединяются.


Вы можете увидеть, что происходит, разобрав байт-код. Попробуйте определить функцию, которая делает это, e, f = 128, 128а затем вызвать dis.disее, и вы увидите, что есть единственное постоянное значение(128, 128)

>>> def f(): i, j = 258, 258
>>> dis.dis(f)
  1           0 LOAD_CONST               2 ((128, 128))
              2 UNPACK_SEQUENCE          2
              4 STORE_FAST               0 (i)
              6 STORE_FAST               1 (j)
              8 LOAD_CONST               0 (None)
             10 RETURN_VALUE
>>> f.__code__.co_consts
(None, 128, (128, 128))
>>> id(f.__code__.co_consts[1], f.__code__.co_consts[2][0], f.__code__.co_consts[2][1])
4305296480, 4305296480, 4305296480

Вы можете заметить, что компилятор хранится 128как константа, даже если он фактически не используется байт-кодом, что дает вам представление о том, как мало делает оптимизация компилятора CPython. Это означает, что (непустые) кортежи в действительности не сливаются:

>>> k, l = (1, 2), (1, 2)
>>> k is l
False

Положим , что в функции, disона, и взгляд на co_consts-Есть это 1и А 2, два (1, 2)кортежи , которые разделяют то же самое 1и , 2но не идентичны, и ((1, 2), (1, 2))кортеж , который имеет два различных равных кортежи.


Есть еще одна оптимизация, которую делает CPython: интернирование строк. В отличие от свертывания констант компилятора, это не ограничивается литералами исходного кода:

>>> m = 'abc'
>>> n = 'abc'
>>> m is n
True

С другой стороны, он ограничен strтипом и строками внутренней памяти типа «ascii compact», «compact» или «legacy ready» , и во многих случаях интернируется только «ascii compact».


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

Может быть стоит изучить правила для одного конкретного Python для удовольствия. Но не стоит полагаться на них в своем коде. Единственное безопасное правило:

  • Не пишите код, в котором предполагается, что два одинаковых, но создаваемых отдельно неизменных значения идентичны (не используйте x is y, используйтеx == y )
  • Не пишите код, который предполагает, что два равных, но отдельно создаваемых неизменяемых значения различны (не используйте x is not y, используйте x != y)

Или, другими словами, используйте только isдля проверки документированных синглетов (например None) или которые создаются только в одном месте кода (например, _sentinel = object()идиома).

abarnert
источник
Менее загадочный совет прост: не используйте x is yдля сравнения, используйте x == y. Также не используйте x is not y, используйтеx != y
smci
Итак, глядя на этот вопрос , почему a=257; b=257на одной строке a is bTrue
Джо
8

is является оператором равенства идентичности (функционирующим как id(a) == id(b)); просто два равных числа не обязательно являются одним и тем же объектом. Из соображений производительности некоторые небольшие целые числа запоминаются, поэтому они будут одинаковыми (это можно сделать, поскольку они неизменяемы).

===Оператор PHP , с другой стороны, описывается как проверка равенства и типа: x == y and type(x) == type(y)согласно комментарию Пауло Фрейтаса. Это будет достаточно для простых чисел, но отличается от isклассов, которые определяют __eq__абсурдным образом:

class Unequal:
    def __eq__(self, other):
        return False

PHP, по-видимому, допускает то же самое для «встроенных» классов (которые я имею в виду, реализованные на уровне C, а не в PHP). Чуть менее абсурдным может быть использование объекта-таймера, значение которого будет отличаться при каждом использовании в качестве числа. Почему вы хотите эмулировать Visual Basic Nowвместо того, чтобы показывать, что это оценка, с которой time.time()я не знаю.

Грег Хьюгилл (OP) сделал один уточняющий комментарий: «Моя цель - сравнить идентичность объекта, а не равенство значений. За исключением чисел, где я хочу трактовать идентичность объекта так же, как равенство значений».

Это даст еще один ответ, поскольку мы должны классифицировать вещи как числа или нет, чтобы выбрать, сравнивать ли мы с ==или is. CPython определяет протокол нумерации , включая PyNumber_Check, но он недоступен из самого Python.

Мы можем попытаться использовать isinstanceвсе известные нам типы чисел, но это неизбежно будет неполным. Модуль types содержит список StringTypes, но не содержит NumberTypes. Начиная с Python 2.6, встроенные числовые классы имеют базовый класс numbers.Number, но у него есть та же проблема:

import numpy, numbers
assert not issubclass(numpy.int16,numbers.Number)
assert issubclass(int,numbers.Number)

Кстати, NumPy будет выдавать отдельные экземпляры низких чисел.

На самом деле я не знаю ответа на этот вариант вопроса. Я полагаю, теоретически можно использовать ctypes для вызова PyNumber_Check, но даже эта функция обсуждалась , и она, безусловно, не переносима. Нам просто нужно быть менее внимательным к тому, что мы сейчас тестируем.

В конце концов, эта проблема связана с тем, что в Python изначально не было дерева типов с предикатами типа Scheme number? или класса типов Haskell Num . проверяет идентичность объекта, а не равенство значений. PHP также имеет красочную историю, где, по- видимому, ведет себя как только на объектах в PHP5, но не PHP4 . Такова растущая боль при перемещении между языками (включая версии одного). is===is

Ян Вернье
источник
4

Это также происходит со строками:

>>> s = b = 'somestr'
>>> s == b, s is b, id(s), id(b)
(True, True, 4555519392, 4555519392)

Теперь все вроде нормально.

>>> s = 'somestr'
>>> b = 'somestr'
>>> s == b, s is b, id(s), id(b)
(True, True, 4555519392, 4555519392)

Это тоже ожидается.

>>> s1 = b1 = 'somestrdaasd ad ad asd as dasddsg,dlfg ,;dflg, dfg a'
>>> s1 == b1, s1 is b1, id(s1), id(b1)
(True, True, 4555308080, 4555308080)

>>> s1 = 'somestrdaasd ad ad asd as dasddsg,dlfg ,;dflg, dfg a'
>>> b1 = 'somestrdaasd ad ad asd as dasddsg,dlfg ,;dflg, dfg a'
>>> s1 == b1, s1 is b1, id(s1), id(b1)
(True, False, 4555308176, 4555308272)

Теперь это неожиданно.

sobolevn
источник
Случилось это - согласился, что даже страннее. Так что я играл с ним, и это еще более странно - связано с космосом. Например, строка 'xx'является ожидаемой, как есть 'xxx', но 'x x'не является.
Брайан
2
Это потому, что это выглядит как символ, если в нем нет пробела. Имена автоматически интернируются, поэтому, если xxв вашем сеансе Python есть что-нибудь названное , эта строка уже интернирована; и может быть эвристика, которая делает это, если это просто напоминает имя. Как и с числами, это можно сделать, потому что они неизменны. docs.python.org/2/library/functions.html#intern guilload.com/python-string-interning
Янн Вернье,
3

Что нового в Python 3.8: Изменения в поведении Python :

Компилятор теперь генерирует SyntaxWarning, когда проверки идентичности ( isи is not) используются с определенными типами литералов (например, строки, целые числа). Они часто могут работать случайно в CPython, но не гарантируются спецификацией языка. Предупреждение рекомендует пользователям вместо этого использовать тесты на равенство ( == и !=).

cclauss
источник