Автор оригинала: Olivera Popović.
‘is’ vs ‘==’ in Python – Object Comparation
‘is’ vs ‘==’ в Python
В Python есть два очень похожих оператора для проверки того, равны ли два объекта. Эти два оператора являются is
и ==
.
Они обычно путаются друг с другом, потому что с простыми типами данных, такими как int
и string
s (с которыми многие люди начинают изучать Python), они, кажется, делают то же самое:
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_list 1
равен //some_list 2 по значению (они оба равны [1]
]), но они не являются идентичными
, что означает, что они не являются одним и тем же объектом, даже если они имеют равные значения.
Однако some_list1
является одновременно равным и идентичным | some_list3 , поскольку они ссылаются на один и тот же объект в памяти.
Изменяемые и Неизменяемые типы данных
Хотя эта часть проблемы теперь может быть ясна (когда мы назвали переменные), может возникнуть другой вопрос:
Как получилось, что is
и ==
ведут себя одинаково с неназванными int
и string
значениями (например 5
и "пример"
), но не ведут себя так же с безымянными списками (например, [1]
)?
В Python существует два типа типов данных – mutable и immutable .
- Изменяемые типы данных-это типы данных, которые вы можете “изменять” с течением времени
- Неизменяемые типы данных остаются неизменными (имеют одну и ту же ячейку памяти, что и проверка
is
) после их создания
Изменяемыми типами данных являются: list
, dictionary
, set
и пользовательские классы.
Неизменяемыми типами данных являются: int
, float
, decimal
, bool
, string
, tuple
и range
.
Как и многие другие языки, Python обрабатывает неизменяемые типы данных иначе, чем изменяемые, то есть сохраняет их в памяти только один раз.
Так что каждый 5
вы используете в своем коде точно такое же 5
вы используете его в других местах своего кода, и то же самое относится к строковым литералам, которые вы используете.
Если вы используете строку "example"
один раз, то каждый раз, когда вы используете "example"
это будет точно такой же объект. См. Это Примечание для получения дополнительных разъяснений.
Мы будем использовать функцию 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
возвращается к тому же самому "примеру"
объекту, которому он был назначен в начале, даже если мы тем временем изменим значение s
.
Однако list
не возвращает тот же объект , значение которого равно [1]
, но создается совершенно новый объект, даже если он имеет то же значение , что и первый [1]
.
Если вы запустите приведенный выше код, вы, скорее всего, получите разные идентификаторы для объектов, но равенства будут одинаковыми.
Когда используются “is” и “==” Соответственно?
Оператор is
чаще всего используется , когда мы хотим сравнить объект с None
, и обычно рекомендуется ограничивать его использование этим конкретным сценарием, если только вы действительно (и я имею в виду действительно) не хотите проверить, идентичны ли два объекта.
Кроме того, is
обычно быстрее, чем оператор ==
, потому что он просто проверяет целочисленное равенство адреса памяти.
Важное примечание: Единственная ситуация, когда is
работает точно так, как можно было бы ожидать, – это с singleton classes/objects (например, None
). Даже с неизменяемыми объектами существуют ситуации, когда is
does not работает так, как ожидалось.
Например, для больших string
объектов, генерируемых некоторой логикой кода, или больших int
s, is
может (и будет) вести себя непредсказуемо. Если вы не приложите усилий к интернированию (то есть не будете абсолютно уверены, что существует только одна копия string
/| int | и т. Д.), Все различные неизменяемые объекты, которые вы планируете использовать,/| is
будут непредсказуемы.
Суть такова: используйте ==
в 99% случаев.
Если два объекта идентичны они также равны , и что противоположное не обязательно верно.
Переопределение!=’ Операторов
Операторы !=
и is not
ведут себя так же, как и их “положительные” аналоги. А именно, !=
возвращает True
если объекты не имеют одного и того же значения, в то время как is not
возвращает True
если объекты не хранятся в одном и том же адресе памяти.
Еще одно различие между этими двумя операторами заключается в том, что вы можете переопределить поведение ==
/ !=
для пользовательского класса, в то время как вы не можете переопределить поведение is
.
Если вы реализуете пользовательский метод __eq()__
в своем классе, вы можете изменить способ ==
/ !=
операторы ведут себя:
class TestingEQ: def __init__(self, n): self.n = n # using the '==' to check whether both numbers # are even, or if both numbers are odd 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
, если вы точно не знаете, что делаете , или когда имеете дело с одноэлементными объектами, такими как None
, так как они могут вести себя непредсказуемо.