== и is в Python: разница, примеры и лучшие практики для начинающих

== и is в Python: разница, примеры и лучшие практики для начинающих

== и is в Python: разница, примеры и лучшие практики для начинающих

Запрос «== vs is в Python» — один из самых частых у начинающих. Несмотря на простоту, эта тема регулярно вызывает ошибки в продакшене. Разберёмся глубоко и практично: что такое равенство и идентичность объектов, где каждый оператор уместен, и как избежать тонких багов.

Коротко: в чём разница между == и is

  • == сравнивает значения объектов (логическое равенство). Может вызывать метод __eq__ у объектов.
  • is сравнивает идентичность — являются ли это одним и тем же объектом в памяти (тот же id()).
  • a = [1, 2, 3]
    b = [1, 2, 3]
    c = a
    
    print(a == b)  # True: значения списков равны
    print(a is b)  # False: это разные объекты
    print(a is c)  # True: c указывает на тот же объект, что и a
    

    Числа и строки: почему иногда «везёт», а иногда — нет

    В CPython интернируются некоторые маленькие объекты (например, целые числа в небольшом диапазоне и строковые литералы). Это оптимизация, а не правило языка. Нельзя на неё рассчитывать в логике программы.

    # Пример с целыми числами
    a = 256
    b = 256
    print(a is b)  # Часто True в CPython (кэш малых int), но это не гарантия стандарта
    
    x = 10_000
    y = 10_000
    print(x is y)  # Часто False: большие int не кешируются одинаково
    
    # Пример со строками
    s1 = "hello"
    s2 = "hello"
    print(s1 is s2)  # Часто True (литералы могут интернироваться)
    
    s3 = "he" + "llo"  # Компилятор может склеить во время компиляции
    print(s1 is s3)  # Может быть True
    
    s4 = "he" + input()  # Формируется во время выполнения
    print(s1 == s4)  # Сравнивайте по значению
    print(s1 is s4)  # Не полагайтесь на это — почти наверняка False
    

    Если вам действительно нужен контроль за интернированием строк (редко нужно), используйте sys.intern.

    import sys
    s1 = sys.intern("very_long_key_value")
    s2 = sys.intern("very_" + "long_key_value")
    print(s1 is s2)  # True: теперь это один и тот же объект в кеше интернировщика
    

    Когда использовать is: None и синглтоны

    Оператор is уместен для сравнения с None и другими синглтонами (уникальными объектами), потому что у них есть ровно один экземпляр.

    result = None
    
    # Правильно
    if result is None:
        print("Пока результата нет")
    
    # Неправильно
    if result == None:  # pep8: E711 — используйте is None
        pass
    

    Аналогично, True и False — синглтоны. Но писать if flag is True: считается дурным тоном — используйте просто if flag:.

    flag = True
    # Плохо
    if flag is True:
        ...
    # Хорошо
    if flag:
        ...
    # Проверка на ложность без is False
    if not flag:
        ...
    

    Пользовательские классы и __eq__: равные, но не идентичные

    Оператор == вызывает метод __eq__ (если он определён). Так объекты могут быть «равны по значению», не будучи одним и тем же объектом.

    class Point:
        def __init__(self, x, y):
            self.x = x
            self.y = y
        def __eq__(self, other):
            if isinstance(other, Point):
                return (self.x, self.y) == (other.x, other.y)
            return NotImplemented
    
    p1 = Point(1, 2)
    p2 = Point(1, 2)
    print(p1 == p2)  # True: равны по значению
    print(p1 is p2)  # False: разные объекты
    

    Если __eq__ не реализован, унаследованное сравнение от object фактически ведёт себя как сравнение идентичности: разные экземпляры будут «не равны» по == даже с одинаковыми полями.

    Контейнеры: == сравнивает поэлементно, is — ссылку на объект

    Списки, кортежи, множества и словари сравниваются по содержимому при помощи ==. Оператор is уместен только для проверки, что это именно тот же самый контейнер (та же ссылка).

    a = [1, 2]
    b = [1, 2]
    c = a
    
    print(a == b)  # True: поэлементное равенство
    print(a is b)  # False
    print(a is c)  # True
    
    # Осторожно с общей ссылкой
    c.append(3)
    print(a)  # [1, 2, 3] — изменился и a, и c, потому что это один объект
    

    Особый случай: NaN — ничему не равен, даже себе

    По стандарту IEEE 754, NaN «не равно ничему, даже самому себе». Поэтому проверки вида x == float('nan') всегда дадут False. Оператор is тут тоже не поможет: создаются разные объекты NaN.

    import math
    nan1 = float('nan')
    nan2 = float('nan')
    
    print(nan1 == nan2)  # False
    print(nan1 is nan2)  # False
    print(math.isnan(nan1))  # True — правильная проверка
    

    В NumPy используйте numpy.isnan или pandas.isna для векторизованных проверок — сравнение через == и тем более is там тоже не подходит.

    Производительность: is быстрее, но выбирайте корректность

    is — очень быстрая операция (сравнивает адреса объектов), но использовать её «для скорости» там, где требуется равенство значений, — ошибка. Сначала корректность, затем — микропроизводительность.

    Частые ошибки и как их исправить

  • Сравнение строк через is: используйте ==.
  • Сравнение чисел через is: используйте == (а для float при необходимости — сравнение с допуском через math.isclose).
  • Проверка на None через ==: используйте is None и is not None.
  • Проверка булей через is True/is False: используйте if flag или if not flag.
  • Опора на интернирование объектов (числа/строки) для логики: это деталь реализации, избегайте.
  • Практическая памятка

  • Нужно сравнить значения? Используйте ==.
  • Нужно проверить, что объект — именно None (или другой синглтон)? Используйте is.
  • С контейнерами: == — по содержимому, is — та же ссылка.
  • С плавающей точкой: для близости значений используйте math.isclose.
  • Для NaN: math.isnan / numpy.isnan / pandas.isna.
  • Небольшие упражнения для закрепления

    # 1) Что выведет код и почему?
    a = 1000
    b = 10**3
    print(a == b)
    print(a is b)
    
    # 2) Исправьте условие так, чтобы корректно проверять отсутствие значения:
    value = None
    if value == None:  # <-- исправить
        print("пусто")
    
    # 3) Почему так и как правильно?
    nan = float('nan')
    print(nan == nan)
    # Сделайте корректную проверку на NaN
    

    Куда двигаться дальше

    Вы разобрались с ключевой парой операторов «== vs is в Python». Следующий шаг — практиковаться на реальных задачах и укреплять базу языка: типы данных, функции, тестирование, работа с файлами и сетевыми запросами. Отличный путь — пройти структурированный практический курс с заданиями и обратной связью.

    Хочу быстро прокачаться на курсе «Программирование на Python с Нуля до Гуру» →

    Выводы

  • == — для сравнения значений; is — для сравнения идентичности объектов.
  • С None всегда используйте is.
  • Не полагайтесь на интернирование чисел и строк для логики.
  • Учитывайте особенности float и NaN; выбирайте правильные функции сравнения.
  • С этими правилами ваши проверки станут предсказуемыми и безопасными в любом окружении Python.

    Источник

    НЕТ КОММЕНТАРИЕВ

    Оставить комментарий