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