Операторы 'is' и '==' в Python

‘is’ и ‘==’ в Python

В Python есть два похожих оператора, предназначенных для сравнения объектов. Эти оператор is и ==. Их часто путают, потому они одинаково сравнивают типы данных intи string:

x = 5
s = "example"

print("x == 5: " + str(x == 5))
print("x is 5: " + str(x is 5))
print("s == 'example': " + str(s == "example"))
print("s is 'example': " + str(s is "example"))

Результат выполнения кода:

x == 5: True
x is 5: True
s == 'example': True
s is 'example': True

Это доказывает, что == и is при таком сравнении возвращают True. Но если сравнить более сложные структуры данных, то получим следующее:

some_list = [1]

print("some_list == [1]: " + str(some_list == [1]))
print("some_list is [1]: " + str(some_list is [1]))

Результат выполнения кода:

some_list == [1]: True
some_list is [1]: False

Разница заключается в том, что is сравнивает идентичность объектов, а ==проверяет равенство значений.

Пример, который показывает разницу между этими двумя операторами.

some_list1 = [1]
some_list2 = [1]
some_list3 = some_list1

print("some_list1 == some_list2: " + str(some_list1 == some_list2))
print("some_list1 is some_list2: " + str(some_list1 is some_list2))
print("some_list1 == some_list3: " + str(some_list1 == some_list3))
print("some_list1 is some_list3: " + str(some_list1 is some_list3))

Результат выполнения:

some_list1 == some_list2: True
some_list1 is some_list2: False
some_list1 == some_list3: True
some_list1 is some_list3: True

some_list1 равен some_list2 по значению ([1]]). Но они не идентичны. Но some_list1одновременно равен и идентичен some_list3, так как они ссылаются на один и тот же объект в памяти.

Изменяемые и неизменяемые типы данных

Только почему операторы is и == одинаково сравнивают неименованные значения intи string (например, 5 и «example»). Но при этом не ведут себя так же с неименованными списками (например, [1])?

В Python есть две разновидности типа данных:

  • Изменяемые — те, которые можно изменять
  • Неизменяемые – остаются неизменными (имеют одинаковое расположение в памяти, что is и проверяет) после их создания.

Изменяемые типы данных: list, dictionary, set и определяемые пользователем классы. Неизменяемые типы данных: int, float, decimal, bool, string, tuple, и range.

Python обрабатывает неизменяемые типы данных иначе. То есть сохраняет их в памяти только один раз.

Применим Python-функцию id(), которая вызывает уникальный идентификатор для каждого объекта:

s = "example"
print("Id of s: " + str(id(s)))
print("Id of the String 'example': " + str(id("example")) + " (note that it's the same as the variable s)")
print("s is 'example': " + str(s is "example"))

print("Change s to something else, then back to 'example'.")
s = "something else"
s = "example"
print("Id of s: " + str(id(s)))
print("s is 'example': " + str(s is "example"))
print()

list1 = [1]
list2 = list1
print("Id of list1: " + str(id(list1)))
print("Id of list2: " + str(id(list2)))
print("Id of [1]: " + str(id([1])) + " (note that it's not the same as list1!)")
print("list1 == list2: " + str(list1 == list2))
print("list1 is list2: " + str(list1 is list2))

print("Change list1 to something else, then back to the original ([1]) value.")
list1 = [2]
list1 = [1]
print("Id of list1: " + str(id(list1)))
print("list1 == list2: " + str(list1 == list2))
print("list1 is list2: " + str(list1 is list2))

Выполнение кода выдаст следующий результат:

Id of s: 22531456
Id of the String 'example': 22531456 (note that it's the same as the variable s)
s is 'example': True
Change s to something else, then back to 'example'.
Id of s: 22531456
s is 'example': True

Id of list1: 22103504
Id of list2: 22103504
Id of [1]: 22104664 (note that it's not the same as list1!)
list1 == list2: True
list1 is list2: True
Change list1 to something else, then back to the original ([1]) value.
Id of list1: 22591368
list1 == list2: True
list1 is list2: False

В первой части примера переменная s возвратит тот же объект "example", которым она была инициализирована в начале, даже если мы изменим ее значение.

Но list не возвращает тот же объект, значение которого равно [1]. При этом создается новый объект, даже если он имеет то же значение, что и первый [1].

При выполнении кода вы получите разные идентификаторы для объектов, но они будут одинаковыми.

Когда используются «is» и «==»?

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

Кроме этого is работает быстрее, чем оператор ==, потому что он просто проверяет адреса памяти на целочисленное равенство.

Важное примечание: Оператор is работает по классической схеме только при сравнении одноэлементных классов и объектов (например, None).

Суть: необходимо использовать == в 99% случаев.

Если два объекта идентичны, они также равны. Но обратное необязательно верно.

Переопределение операторов ‘==’ и ‘!=’

Операторы != и is not ведут себя так же, как и их противоположности. То есть, != возвращает True, если объекты не имеют одинаковое значение. Оператор is not возвращает True, если объекты не хранятся в одном и том же адресе памяти.

Еще одно различие между этими операторами заключается в том, что можно переопределить поведение == / != для пользовательского класса. Но нельзя переопределить поведение is.

Если вы реализуете в пользовательском классе собственный метод __eq()__, то сможете изменить поведение операторов == / !=:

class TestingEQ:
    def __init__(self, n):
        self.n = n

    # используем '==', чтобы проверить четны ли оба числа
    # или нечетны
    def __eq__(self, other):
        if (self.n % 2 == 0 and other % 2 == 0):
            return True
        else:
            return False


print(5 == TestingEQ(1))
print(2 == TestingEQ(10))
print(1 != TestingEQ(2))

Выполнение кода дает следующий результат:

False
True
True

Заключение

Операторы == / != проверяют элементы на равенство по значению. Операторы is / is not проверяют, идентичны ли два объекта (проверяются их адреса в памяти).

Но избегайте использования is, когда имеете дело с одноэлементными объектами. Так как этот оператор может вести себя непредсказуемо.

Пожалуйста, оставляйте свои отзывы по текущей теме материала. За комментарии, отклики, подписки, дизлайки, лайки низкий вам поклон!

Данная публикация является переводом статьи «'is' vs '==' in Python - Object Comparison» , подготовленная редакцией проекта.

Меню