Операторы '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, когда имеете дело с одноэлементными объектами. Так как этот оператор может вести себя непредсказуемо.