is или ==: тождество против равенства и кеш малых int
Очень частый каверзный вопрос: чем is отличается от == и почему is иногда «врёт» на числах.
==сравнивает значения (равны ли объекты по содержимому).isсравнивает тождество — это буквально один и тот же объект в памяти (одинаковыйid()).
Вопрос: в чём разница и когда что использовать?
Чёткий ответ. == — для сравнения значений (почти всегда именно это вам нужно). is — только для сравнения с синглтонами: x is None, x is True, x is False. Сравнивать через is числа и строки — ошибка, даже если иногда «работает».
a = [1, 2, 3]
b = [1, 2, 3]
print("a == b:", a == b) # значения равны
print("a is b:", a is b) # но это разные объекты
c = a
print("a is c:", a is c) # c — то же самое имя того же объекта
Вывод:
a == b: True a is b: False a is c: True
Ловушка: кеш малых целых чисел
CPython кеширует маленькие целые от -5 до 256 — для них всегда возвращается один и тот же объект. Поэтому is на таких числах даёт True, а на числах вне диапазона — False. Это деталь реализации, на неё нельзя опираться.
a = int("256")
b = int("256")
print("256 is 256:", a is b) # в кеше
c = int("257")
d = int("257")
print("257 is 257:", c is d) # вне кеша — разные объекты
print("значения равны:", c == d)
Вывод:
256 is 256: True 257 is 257: False значения равны: True
Здесь int("256") заставляет создать число во время выполнения. Видно: 256 попадает в кеш и оба имени указывают на один объект, а 257 — нет. Если бы мы сравнивали через ==, результат был бы предсказуем всегда.
Правило для собеседования
| Сравниваем с | Оператор |
None, True, False | is / is not |
| числа, строки, списки, объекты — по значению | == |
| «это буквально тот же объект?» | is |
x = None
print(x is None) # правильная проверка на None
value = 0
print(value == 0) # сравнение значения
print(value is None) # 0 — это не None
Вывод:
True True False
Итог
==— равенство значений;is— тождество объектов (одинid).isиспользуйте только сNone/True/False.- Кеш малых int (−5..256) делает
isна числах обманчивым — не полагайтесь на него.