Python '!=' — это не 'is not': сравнение объектов в Python
Оглавление
- Сравнение Идентичности с Python является и не является операторами
- Сравнение равенства с операторами Python == и !=
- Сравнение операторов сравнения Python
- Заключение
Существует небольшая разница между оператором идентификации Python (is
) и оператором равенства (==
). Ваш код может работать нормально, когда вы используете оператор Python is
для сравнения чисел, до тех пор, пока он внезапно не перестанет быть . Возможно, вы где-то слышали, что оператор Python is
работает быстрее, чем оператор ==
, или вам может показаться, что он выглядит более питоническим. Однако важно иметь в виду, что эти операторы ведут себя по-разному.
Оператор ==
сравнивает значение или равенство двух объектов, тогда как оператор Python is
проверяет, соответствуют ли два переменные указывают на один и тот же объект в памяти. В подавляющем большинстве случаев это означает, что вы должны использовать операторы равенства ==
и !=
, за исключением случаев, когда вы сравниваете с None
.
В этом уроке вы узнаете:
- В чем разница между равенством объектов и идентичностью
- Когда следует использовать операторы равенства и идентификации для сравнения объектов
- Что эти Операторы Python делают под капотом
- Почему использование
is
иis not
для сравнения значений приводит к неожиданному поведению - Как написать пользовательский
__eq__()
метод класса для определения поведения оператора равенства
Пит-стоп по Python: Это руководство является кратким и практичным это отличный способ найти нужную вам информацию, чтобы вы могли быстро вернуться к своему проекту!
Бесплатный бонус: Нажмите здесь, чтобы получить шпаргалку по Python и изучить основы Python 3, такие как работа с типами данных, словари, списки и функции Python.
Сравнение Identity С Python является и не является операторами
Операторы Python is
и is not
сравнивают идентичность двух объектов. В CPython это их адрес в памяти. Все в Python является объектом, и каждый объект хранится в определенной ячейке памяти. Операторы Python is
и is not
проверяют, ссылаются ли две переменные на один и тот же объект в памяти.
Примечание: Имейте в виду, что объекты с одинаковым значением обычно хранятся по разным адресам памяти.
Вы можете использовать id()
для проверки идентичности объекта:
>>> help(id)
Help on built-in function id in module builtins:
id(obj, /)
Return the identity of an object.
This is guaranteed to be unique among simultaneously existing objects.
(CPython uses the object's memory address.)
>>> id(id)
2570892442576
В последней строке указан адрес памяти, где хранится сама встроенная функция id
.
Есть несколько распространенных случаев, когда объекты с одинаковым значением по умолчанию будут иметь одинаковый идентификатор. Например, числа от -5 до 256 являются интернированными в CPython. Каждое число хранится в единственном и неизменном месте в памяти, что экономит память для часто используемых целых чисел.
Вы можете использовать sys.intern()
для интернирования строк для повышения производительности. Эта функция позволяет вам сравнивать их адреса в памяти, а не сравнивать строки посимвольно:
>>> from sys import intern
>>> a = 'hello world'
>>> b = 'hello world'
>>> a is b
False
>>> id(a)
1603648396784
>>> id(b)
1603648426160
>>> a = intern(a)
>>> b = intern(b)
>>> a is b
True
>>> id(a)
1603648396784
>>> id(b)
1603648396784
Переменные a
и b
изначально указывают на два разных объекта в памяти, о чем свидетельствуют их разные идентификаторы. Когда вы их внедряете, вы гарантируете, что a
и b
указывают на один и тот же объект в памяти. Любая новая строка со значением 'hello world'
теперь будет создана в новой ячейке памяти, но когда вы вводите эту новую строку, убедитесь, что она указывает на тот же адрес памяти, что и первая 'hello world'
которую вы интернировали.
Примечание: Несмотря на то, что адрес памяти объекта уникален в любой момент времени, он меняется при запуске одного и того же кода и зависит от версии CPython и компьютера, на котором он выполняется .
Другими объектами, которые интернируются по умолчанию, являются None
, True
, False
, и простые строки. Имейте в виду, что в большинстве случаев разные объекты с одинаковым значением будут храниться по разным адресам памяти. Это означает, что вам не следует использовать оператор Python is
для сравнения значений.
Когда Интернированы Только Некоторые Целые Числа
За кулисами Python объединяет объекты с часто используемыми значениями (например, целыми числами от -5 до 256), чтобы сэкономить память. Следующий фрагмент кода показывает вам, что только некоторые целые числа имеют фиксированный адрес в памяти:
>>> a = 256
>>> b = 256
>>> a is b
True
>>> id(a)
1638894624
>>> id(b)
1638894624
>>> a = 257
>>> b = 257
>>> a is b
False
>>> id(a)
2570926051952
>>> id(b)
2570926051984
Изначально a
и b
указывают на один и тот же интернированный объект в памяти, но когда их значения выходят за пределы диапазона общих целых чисел (в диапазоне от -5 до 256), они хранятся по отдельным адресам памяти.
Когда несколько переменных указывают на один и тот же объект
Когда вы используете оператор присваивания (=
) чтобы сделать одну переменную равной другой, вы заставляете эти переменные указывать на один и тот же объект в памяти. Это может привести к неожиданному поведению для изменяемых объектов:
>>> a = [1, 2, 3]
>>> b = a
>>> a
[1, 2, 3]
>>> b
[1, 2, 3]
>>> a.append(4)
>>> a
[1, 2, 3, 4]
>>> b
[1, 2, 3, 4]
>>> id(a)
2570926056520
>>> id(b)
2570926056520
Что только что произошло? Вы добавили новый элемент в a
, но теперь b
также содержит этот элемент! Итак, в строке, где b = a
, вы устанавливаете значение b
, указывающее на тот же адрес памяти, что и a
, так что обе переменные теперь ссылаются на один и тот же объект.
Если вы определяете эти списки независимо друг от друга, то они хранятся по разным адресам памяти и ведут себя независимо:
>>> a = [1, 2, 3]
>>> b = [1, 2, 3]
>>> a is b
False
>>> id(a)
2356388925576
>>> id(b)
2356388952648
Поскольку a
и b
теперь ссылаются на разные объекты в памяти, изменение одного не влияет на другой.
Сравнение равенства с операторами Python == и !=
Напомним, что объекты с одинаковым значением часто хранятся по разным адресам памяти. Используйте операторы равенства ==
и !=
, если вы хотите проверить, имеют ли два объекта одинаковое значение, независимо от того, где они хранятся в памяти. В подавляющем большинстве случаев это именно то, что вы хотите сделать.
Когда Копия Объекта Равна, но Не Идентична
В приведенном ниже примере вы устанавливаете b
как копию a
(который является изменяемым объектом, таким как список или словарь). Обе переменные будут иметь одинаковое значение, но каждая из них будет храниться по другому адресу в памяти:
>>> a = [1, 2, 3]
>>> b = a.copy()
>>> a
[1, 2, 3]
>>> b
[1, 2, 3]
>>> a == b
True
>>> a is b
False
>>> id(a)
2570926058312
>>> id(b)
2570926057736
a
и b
теперь хранятся по разным адресам памяти, поэтому a is b
больше не будет возвращать True
. Однако a == b
возвращает True
, поскольку оба объекта имеют одинаковое значение.
Как работает сравнение по равенству
Волшебство оператора равенства ==
проявляется в методе класса __eq__()
объекта, расположенного слева от знака ==
.
Примечание: Это так, если только объект справа не является подклассом объекта слева. Для получения дополнительной информации ознакомьтесь с официальной документацией .
Это метод волшебного класса, который вызывается всякий раз, когда экземпляр этого класса сравнивается с другим объектом. Если этот метод не реализован, то ==
сравнивает адреса памяти двух объектов по умолчанию.
В качестве упражнения создайте класс SillyString
, который наследуется от str
, и реализуйте __eq__()
, чтобы сравнить, совпадает ли длина этой строки с длиной другого объекта:
class SillyString(str):
# This method gets called when using == on the object
def __eq__(self, other):
print(f'comparing {self} to {other}')
# Return True if self and other have the same length
return len(self) == len(other)
Теперь глупая строка 'hello world'
должна быть равна строке 'world hello'
и даже любому другому объекту такой же длины:
>>> # Compare two strings
>>> 'hello world' == 'world hello'
False
>>> # Compare a string with a SillyString
>>> 'hello world' == SillyString('world hello')
comparing world hello to hello world
True
>>> # Compare a SillyString with a list
>>> SillyString('hello world') == [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]
comparing hello world to [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]
True
Это, конечно, глупое поведение для объекта, который в противном случае ведет себя как строка, но оно иллюстрирует, что происходит, когда вы сравниваете два объекта, используя ==
. Оператор !=
выдает обратный результат, если только не реализован конкретный метод класса __ne__()
.
Приведенный выше пример также наглядно показывает, почему рекомендуется использовать оператор Python is
для сравнения с None
вместо оператора ==
. Это не только быстрее, поскольку сравнивает адреса памяти, но и безопаснее, поскольку не зависит от логики каких-либо методов класса __eq__()
.
Сравнение операторов сравнения Python
Как правило, вы всегда должны использовать операторы равенства ==
и !=
, за исключением случаев, когда вы сравниваете с None
:
-
Используйте операторы Python
==
и!=
для сравнения равенства объектов. Здесь вы, как правило, сравниваете значение двух объектов. Это то, что вам нужно, если вы хотите сравнить, имеют ли два объекта одинаковое содержимое, и вас не волнует, где они хранятся в памяти. -
Используйте операторы Python
is
иis not
, когда вы хотите сравнить идентичность объекта. Здесь вы сравниваете, указывают ли две переменные на один и тот же объект в памяти. Основной вариант использования этих операторов - когда вы сравниваете сNone
. Быстрее и безопаснее сравнивать сNone
по адресу памяти, чем с помощью методов класса.
Переменные с одинаковым значением часто хранятся по разным адресам памяти. Это означает, что вам следует использовать ==
и !=
для сравнения их значений и использовать операторы Python is
и is not
только тогда, когда вы хотите проверить, указывают ли две переменные на один и тот же адрес памяти.
Заключение
В этом руководстве вы узнали, что ==
и !=
сравнивают значения двух объектов, тогда как в Python is
операторы > и is not
сравнивают, ссылаются ли две переменные на один и тот же объект в памяти. Если вы будете помнить об этом различии, то сможете предотвратить неожиданное поведение в своем коде.
Если вы хотите узнать больше об удивительном мире интернирования объектов и операторе Python is
, ознакомьтесь с почему вам следует почти никогда не используйте “is” в Python. Вы также могли бы взглянуть на то, как вы можете использовать sys.intern()
для оптимизации использования памяти и времени сравнения строк, хотя есть вероятность, что Python уже автоматически обрабатывает это за вас за кулисами.
Теперь, когда вы узнали, что делают операторы равенства и идентификации, вы можете попробовать написать свои собственные __eq__()
методы класса, которые определяют, как экземпляры этого класса работают. сравниваются при использовании оператора ==
. Идите и примените свои новообретенные знания об этих операторах сравнения Python!