Если спросить начинающего Python-разработчика, чем отличаются операторы is и ==, то чаще всего можно услышать что-то вроде:
`==` сравнивает значения, а `is` сравнивает ссылки.
Это распространённый ответ. И хотя в нём есть доля правды, он не совсем точный.
Давайте разберёмся, что происходит на самом деле.
Начнём с простого примера.
a = [1, 2, 3]
b = [1, 2, 3]
print(a == b)
print(a is b)
Результат будет таким:
True
False
На первый взгляд это выглядит странно.
Если списки одинаковые, почему один оператор говорит True, а другой - False?
Чтобы ответить на этот вопрос, давайте вспомним то, что мы уже знаем из предыдущих статей.
Каждая переменная в Python содержит не объект, а имя, которое на него ссылается.
Когда мы выполняем такой код:
a = [1, 2, 3]
создаётся объект списка.
Затем выполняется следующая строка:
b = [1, 2, 3]
И здесь создаётся ещё один объект списка.
Несмотря на то что оба списка содержат одинаковые элементы, это разные объекты.
Схематично это можно представить так:
a ───► [1, 2, 3]
b ───► [1, 2, 3]
Теперь становится понятнее поведение операторов.
Оператор == задаёт вопрос:
Эти объекты содержат одинаковые данные?
В нашем случае ответ - да.
Поэтому:
a == b
возвращает
True
Оператор is задаёт совсем другой вопрос:
Это один и тот же объект?
И здесь ответ уже отрицательный.
Поэтому:
a is b
возвращает
False
Получается, что == сравнивает содержимое объектов, а is - сам объект.
Посмотрим теперь на другой пример.
a = [1, 2, 3]
b = a
print(a == b)
print(a is b)
Результат изменится:
True
True
Почему?
Потому что теперь второй список вообще не создавался.
Строка
b = a
лишь дала существующему объекту ещё одно имя.
Теперь схема выглядит так:
a ─┐
├──► [1, 2, 3]
b ─┘
Именно поэтому оба оператора возвращают True.
Объект один.
И данные у него тоже одни.
На этом этапе может возникнуть вполне логичный вопрос.
Если is проверяет, являются ли объекты одним и тем же объектом, зачем вообще нужен такой оператор?
Разве нельзя всегда использовать ==?
В большинстве случаев действительно нужно использовать именно ==.
Например, если вы хотите узнать, равны ли два числа, две строки или два списка по содержимому.
Но иногда важно проверить не содержимое, а именно объект.
Самый известный пример - сравнение с None.
Наверняка вы встречали такой код:
if value is None:
...
Именно здесь используется is, а не ==.
Почему?
Потому что None существует в программе в единственном экземпляре.
Нас интересует не объект, похожий на None, а именно тот самый объект None.
Поэтому Python-сообщество рекомендует всегда писать именно так:
if value is None:
а не
if value == None:
Это считается хорошим стилем и сразу показывает намерение программиста.
Иногда можно встретить ещё более странную ситуацию.
Попробуйте выполнить такой код:
a = 10
b = 10
print(a is b)
На многих компьютерах результат окажется таким:
True
Хотя, если следовать нашим объяснениям, мы могли ожидать False.
Не спешите делать выводы.
Это не означает, что числа работают иначе.
Дело в том, что Python иногда переиспользует некоторые небольшие объекты для ускорения работы программы.
Это внутренняя оптимизация языка.
Важно понимать, что писать код, рассчитывая на такое поведение, нельзя.
Поэтому, если вам нужно сравнить значения, используйте ==.
Если же вы хотите узнать, являются ли две переменные ссылками на один и тот же объект, используйте is.
Теперь можно сформулировать простое правило, которое легко запомнить.
Если вы спрашиваете:
"Одинаковые ли это данные?"
используйте ==.
Если же вопрос звучит так:
"Это один и тот же объект?"
используйте is.
Именно поэтому предыдущие статьи были так важны.
Без понимания объектов и ссылок различие между is и == приходится просто запоминать.
Но теперь оно выглядит совершенно естественным.