Python с первых версий позволял сравнивать объекты между собой. Для описания, что значит "больше" или "равно" для ваших объектов, были созданы специальные методы сравнения (eq, ne, lt, le, gt, ge). C появлением PEP 207 (Python 2.1+) был реализован единый протокол сравнения, чтобы программисты могли явно контролировать поведение при сравнениях.
Стандартные типы (int, str) сравниваются "по значению", но пользовательские классы по умолчанию сравниваются "по идентичности" (is). Если не реализовать или реализовать неверно магические методы сравнения, сравнение ваших объектов даст неожиданное поведение при работе с коллекциями, сортировкой, использованием set и dict, а иногда приведет к ошибкам типов.
Необходимо явно определить методы сравнения в вашем классе. Чаще всего реализуют eq (равенство) и lt (меньше), а остальные методы можно получить автоматически при помощи декоратора functools.total_ordering.
Пример кода:
from functools import total_ordering @total_ordering class Point: def __init__(self, x, y): self.x = x self.y = y def __eq__(self, other): if not isinstance(other, Point): return NotImplemented return self.x == other.x and self.y == other.y def __lt__(self, other): if not isinstance(other, Point): return NotImplemented return (self.x, self.y) < (other.x, other.y) # Использование: p1 = Point(1, 2) p2 = Point(1, 3) print(p1 < p2) # True print(p1 == p2) # False
Ключевые особенности:
Можно ли реализовать только один метод сравнения (eq или lt), и все будет работать?
Нет. Реализовав только eq, вы обеспечите корректное сравнение на равенство, но операторы <, >, <=, >= будут вести себя стандартно (обычно через сравнение идентификаторов объектов). Для полного поведения следует реализовать достаточный набор методов, либо воспользоваться functools.total_ordering.
Почему compare(x, y) не существует в Python, как в других языках?
Python унаследовал идею специальных "магических" методов для каждого оператора, а функция cmp была удалена начиная с Python 3. Для общего сравнения объектов используют rich comparison методы (lt, eq и др.), а для сортировки — параметр key в функциях sort/sorted.
Что будет, если сравнивать объект с типом, для которого не реализован нужный метод?
Если в eq или других методах возвращать NotImplemented при неподдерживаемых типах, Python автоматически попробует обратный метод у второго операнда или вернет False. Если не обрабатывать такой случай, возможен TypeError или совсем неверное поведение.
Пример кода:
class Foo: def __eq__(self, other): return NotImplemented class Bar: pass print(Foo() == Bar()) # False (проба через Bar.__eq__)
Разработчик создал класс User и реализовал только eq без hash, чтобы пользователи считались равными по email. Затем попытался использовать объекты User в set/в качестве ключей словаря.
Плюсы:
Минусы:
Программист использует @total_ordering и реализует eq и lt с проверкой типа, а также добавляет hash, если класс неизменяемый. Объекты корректно сравниваются, сортируются, работают как ключи словаря.
Плюсы:
Минусы: