Есть ли разница между ==
и is
в Python?
Да, у них есть очень важное различие.
==
: проверьте на равенство - семантика состоит в том, что эквивалентные объекты (которые не обязательно являются одним и тем же объектом) будут проверяться как равные. Как сказано в документации :
Операторы <,>, ==,> =, <= и! = Сравнивают значения двух объектов.
is
: проверьте идентичность - семантика заключается в том, что объект (как хранится в памяти) является объектом. Опять же, в документации сказано :
Операторы is
и is not
тест для идентификации объекта: x is y
истинно тогда и только тогда, когда x
и y
являются одним и тем же объектом. Идентичность объекта определяется с помощью id()
функции. x is not y
дает обратное значение истины.
Таким образом, проверка на идентичность аналогична проверке на равенство идентификаторов объектов. Это,
a is b
такой же как:
id(a) == id(b)
где id
- встроенная функция, которая возвращает целое число, которое «гарантированно будет уникальным среди одновременно существующих объектов» (см. help(id)
) и где a
и b
- любые произвольные объекты.
Другие направления использования
Вы должны использовать эти сравнения для их семантики. Используйте, is
чтобы проверить идентичность и ==
проверить равенство.
В общем, мы используем is
для проверки личности. Это обычно полезно, когда мы проверяем объект, который должен существовать только один раз в памяти, называемый в документации «синглтоном».
Варианты использования для is
включают в себя:
None
- значения enum (при использовании Enums из модуля enum)
- обычно модули
- обычно объекты класса, полученные из определений классов
- обычно функциональные объекты, вытекающие из определений функций
- все остальное, что должно существовать в памяти только один раз (как правило, все синглтоны)
- конкретный объект, который вы хотите по идентичности
Обычные варианты использования ==
включают в себя:
- числа, включая целые числа
- строки
- списки
- наборы
- словари
- пользовательские изменяемые объекты
- другие встроенные неизменяемые объекты, в большинстве случаев
Общий случай использования, опять же , для ==
, это объект , который вы хотите , не может быть тот же объект, а это может быть эквивалентно один
PEP 8 направлений
PEP 8, официальное руководство по стилю Python для стандартной библиотеки, также упоминает два варианта использования дляis
:
Сравнения с синглетонами, такими как None
всегда, должны выполняться с is
или
is not
никогда, а не с операторами равенства.
Кроме того, остерегайтесь писать, if x
когда вы действительно имеете в виду if x is not None
- например, когда проверяете, было ли для переменной или аргумента по умолчанию None
задано какое-то другое значение. Другое значение может иметь тип (например, контейнер), который может быть ложным в логическом контексте!
Вывод равенства из идентичности
Если is
это правда, равенство обычно можно вывести - логически, если объект сам по себе, то он должен проверяться как эквивалентный самому себе.
В большинстве случаев эта логика верна, но она опирается на реализацию __eq__
специального метода. Как говорят документы ,
Поведение по умолчанию для сравнения равенства ( ==
и !=
) основано на идентичности объектов. Следовательно, сравнение равенства экземпляров с одинаковой идентичностью приводит к равенству, а сравнение равенства экземпляров с разными идентичностями приводит к неравенству. Мотивация для этого поведения по умолчанию - желание, чтобы все объекты были рефлексивными (то есть, х есть у, подразумевает х == у).
и в интересах последовательности рекомендует:
Сравнение равенства должно быть рефлексивным. Другими словами, идентичные объекты должны сравниваться равными:
x is y
подразумевает x == y
Мы можем видеть, что это поведение по умолчанию для пользовательских объектов:
>>> class Object(object): pass
>>> obj = Object()
>>> obj2 = Object()
>>> obj == obj, obj is obj
(True, True)
>>> obj == obj2, obj is obj2
(False, False)
Противоположное также обычно верно - если что-то проверяется как не равное, вы можете сделать вывод, что это не один и тот же объект.
Поскольку тесты на равенство могут быть настроены, этот вывод не всегда верен для всех типов.
Исключение
Заметное исключение nan
- оно всегда тестируется как не равное себе:
>>> nan = float('nan')
>>> nan
nan
>>> nan is nan
True
>>> nan == nan # !!!!!
False
Проверка на идентичность может быть намного быстрее, чем проверка на равенство (что может потребовать рекурсивной проверки членов).
Но его нельзя заменить равенством, когда вы можете найти более одного объекта в качестве эквивалента.
Обратите внимание, что сравнение равенства списков и кортежей предполагает, что идентичность объектов одинакова (потому что это быстрая проверка). Это может создать противоречия, если логика противоречива - как это для nan
:
>>> [nan] == [nan]
True
>>> (nan,) == (nan,)
True
Поучительная история:
Вопрос пытается использовать is
для сравнения целых чисел. Не следует предполагать, что экземпляр целого числа совпадает с экземпляром, полученным по другой ссылке. Эта история объясняет почему.
У комментатора был код, который основывался на том факте, что маленькие целые числа (от -5 до 256 включительно) в Python являются синглетонами вместо проверки на равенство.
Вау, это может привести к некоторым коварным ошибкам. У меня был какой-то код, который проверял, является ли a b, и работал, как я хотел, потому что a и b, как правило, небольшие числа. Ошибка произошла только сегодня, после шести месяцев работы, потому что a и b были достаточно большими, чтобы их нельзя было кэшировать. - GWG
Это работало в разработке. Возможно, прошло несколько юнит-тестов.
И он работал в производстве - до тех пор, пока код не проверил целое число больше 256, и в этот момент он не заработал.
Это производственный сбой, который мог быть обнаружен при проверке кода или, возможно, при проверке стиля.
Позвольте мне подчеркнуть: не используйте is
для сравнения целых чисел.
echo 'import sys;tt=sys.argv[1];print(tt is "foo", tt == "foo", id(tt)==id("foo"))'| python3 - foo
выход:False True False
.