Короткий ответ
== сравнивает значения объектов через метод __eq__, а is сравнивает идентичность — указывают ли переменные на один и тот же объект в памяти (совпадает ли id()); два разных объекта могут быть равны по значению, но не быть одним и тем же объектом.
Как это работает подробнее
a == b вызывает a.__eq__(b) (с запасным вариантом b.__eq__(a)), и поведение зависит от того, как класс объекта определяет равенство. a is b — это сравнение id(a) == id(b), то есть равенство адресов/идентификаторов объектов, которое ни один класс переопределить не может.
Пример кода
example.py
a = [1, 2, 3]
b = [1, 2, 3]
print(a == b) # True — одинаковое содержимое
print(a is b) # False — это два разных объекта в памяти
c = a
print(a is c) # True — c ссылается на тот же объект
x = 256
y = 256
print(x is y) # True на CPython (кеш маленьких int) — деталь реализации
x2 = 1000
y2 = 1000
print(x2 is y2) # не гарантировано True — нельзя полагаться на это в кодеЧто использовать на практике
- Практика.
isиспользуют для сравнения с синглтонами:is None,is True/False(хотя для bool обычно пишут простоif flag:), и иногда для проверки "это тот же самый объект, а не его копия". Для сравнения значений — чисел, строк, списков, собственных объектов — всегда используют==.
Подводные камни
- Риски. Пишут
if x == Noneвместоif x is None(работает, но не идиоматично и может дать неожиданный результат, если у объекта переопределён__eq__). Другая ошибка — полагаться наisдля сравнения небольших чисел или коротких строк из-за того, что CPython кеширует маленькие int (-5..256) и некоторые строковые литералы: такое поведение является деталью реализации CPython, а не гарантией языка, и ломается, например, для чисел вне диапазона кеша.