← Назад к вопросам

Чем отличаются операторы == и is в Python?

2.0 Middle🔥 121 комментариев
#DevOps и инфраструктура#Django

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI23 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Чем отличаются операторы == и is в Python?

Это один из самых важных различий в Python, которое нужно понимать для корректной работы с объектами. Операторы выглядят похоже, но работают совершенно по-разному.

Основное различие

== (equals) — сравнивает ЗНАЧЕНИЯ

  • Проверяет, равны ли содержимое объектов
  • Использует метод __eq__()
  • Возвращает True, если значения одинаковые

is — сравнивает ИДЕНТИЧНОСТЬ (адреса памяти)

  • Проверяет, указывают ли переменные на один и тот же объект
  • Не вызывает методы, сравнивает адреса в памяти
  • Возвращает True, только если это один и тот же объект

Примеры

# Пример 1: Списки
a = [1, 2, 3]
b = [1, 2, 3]
c = a

print(a == b)   # True (содержимое одинаковое)
print(a is b)   # False (разные объекты в памяти)
print(a is c)   # True (одна переменная указывает на другую)

print(id(a))    # Адрес объекта a
print(id(b))    # Другой адрес
print(id(c))    # Тот же адрес, что и у a

Визуально

=== С == ===
a = [1, 2, 3]  -----> [1, 2, 3] (объект в памяти)
b = [1, 2, 3]  -----> [1, 2, 3] (другой объект в памяти)

a == b  # True (значения совпадают)
a is b  # False (разные адреса)

=== С is ===
a = [1, 2, 3]  ---->
b = a          ----+  (одна ссылка)

a is b  # True (одна и та же ячейка памяти)

Практические примеры

Числа и строки (интернирование)

# Для маленьких чисел Python кэширует объекты
a = 256
b = 256
print(a == b)   # True
print(a is b)   # True (оба указывают на закэшированный объект)

# Для больших чисел
x = 257
y = 257
print(x == y)   # True
print(x is y)   # False (в REPL обычно True, но в модуле False)

# Строки
s1 = "hello"
s2 = "hello"
print(s1 == s2) # True
print(s1 is s2) # True (интернирование строк)

# Но если строки созданы динамически
s3 = "".join(["h", "e", "l", "l", "o"])
print(s1 == s3) # True
print(s1 is s3) # False (динамическая строка не интернируется)

Более сложные объекты

class Person:
    def __init__(self, name):
        self.name = name
    
    def __eq__(self, other):
        return isinstance(other, Person) and self.name == other.name

p1 = Person("Alice")
p2 = Person("Alice")
p3 = p1

print(p1 == p2)   # True (переопределили __eq__, сравниваем имена)
print(p1 is p2)   # False (разные объекты)
print(p1 is p3)   # True (один объект)

Таблица истины

Operation    | Проверяет         | Использует       | Применение
-------------|------------------|------------------|------------------
==           | Значение         | __eq__() метод   | Равенство данных
is           | Идентичность     | id() функция     | Проверка None, True, False
!=           | Не равно         | __ne__() метод   | Неравенство
is not       | Не идентично     | id() функция     | Проверка не-None

Правильное использование

# ПРАВИЛЬНО: сравнение с None, True, False
if x is None:
    pass

if x is True:  # специальный случай
    pass

if x is False:
    pass

# НЕПРАВИЛЬНО (хотя часто работает):
if x == None:   # ПЛОХО, используй 'is None'
    pass

# ПРАВИЛЬНО: сравнение значений
if a == b:
    pass

if "hello" == user_input:
    pass

if [1, 2, 3] == my_list:
    pass

Производительность

import timeit

# is — очень быстро (просто сравнение адресов)
print(timeit.timeit('None is None', number=10000000))  # ~0.2 секунды

# == — медленнее (вызывает метод)
print(timeit.timeit('[1,2,3] == [1,2,3]', number=100000))  # ~0.5 секунд

Поэтому для None, True, False ВСЕГДА используй is.

Кэширование и интернирование

Python оптимизирует память для часто используемых значений:

# Целые числа от -5 до 256 всегда кэшируются
a = 10
b = 10
print(a is b)  # True

# Строки
string_intern = sys.intern("my_string")
# Интернированные строки сравниваются по идентичности быстрее

# Списки и словари НИКОГДА не интернируются
list1 = [1, 2, 3]
list2 = [1, 2, 3]
print(list1 is list2)  # False (всегда)

Переопределение eq

class Point:
    def __init__(self, x, y):
        self.x = x
        self.y = y
    
    def __eq__(self, other):
        """Два точки равны, если их координаты совпадают."""
        if not isinstance(other, Point):
            return False
        return self.x == other.x and self.y == other.y
    
    def __hash__(self):
        """Если переопределили __eq__, нужно переопределить __hash__."""
        return hash((self.x, self.y))

p1 = Point(1, 2)
p2 = Point(1, 2)
p3 = p1

print(p1 == p2)   # True
print(p1 is p2)   # False
print(p1 is p3)   # True

# Теперь Point можно использовать в set и dict
points = {p1, p2}  # Будет один элемент, т.к. p1 == p2
print(len(points))  # 1

Проверка знаний на интервью

# Вопрос: Что выведет?
x = []
y = []

print(x == y)  # True (пустые списки равны по значению)
print(x is y)  # False (разные объекты)

# Вопрос: Что выведет?
a = None
b = None

print(a == b)  # True
print(a is b)  # True (None — единственный объект)

# Вопрос: Правильная ли проверка?
if my_var is None:  # ПРАВИЛЬНО
    pass

if my_var == None:  # ПЛОХАЯ ПРИВЫЧКА
    pass

Когда использовать что

СитуацияОператорОбъяснение
Проверка Noneis NoneСтандарт Python
Проверка True/False== (или просто if x:)Обычно == или без проверки
Сравнение чисел==Значения могут быть равны
Сравнение строк==Сравнивают содержимое
Сравнение списков==Сравнивают элементы
Проверка одного объектаisИдентичность памяти
КэшированиеisОптимизация по памяти

Лучшие практики

# Хорошо
if value is None:
    pass

if value is not None:
    pass

if a == b:  # Для сравнения значений
    pass

# Плохо
if value == None:
    pass

if value != None:
    pass

if not (a is b):  # Используй 'is not'
    pass

Итоговая таблица

Выражение      Проверяет             Используй когда
==             Значения              Сравниваешь данные
!=             Не равны              Противоположно ==
is             Идентичность объекта  Проверяешь None, True, False
is not         Не идентичны          Проверяешь не-None, не-True
Чем отличаются операторы == и is в Python? | PrepBro