Методы для сравнения объектов

В этой лекции мы научимся сравнивать объекты пользовательских классов между собой. По умолчанию экземпляры классов в Python не поддерживают операции сравнения. Если попробовать сравнить два объекта через операторы ==, !=, >, <, >=, или <=, Python выбросит исключение.

Пример ошибки при попытке сравнения двух объектов класса Shape с использованием оператора >:

TypeError: '>' not supported between instances of 'Shape' and 'Shape'

Причина ошибки в том, что для выполнения операций сравнения в классе должны быть определены специальные магические методы. Для каждого оператора существует свой метод, как показано в таблице ниже:

ОператорМагический метод
==__eq__
!=__ne__
<__lt__
<=__le__
>__gt__
>=__ge__

Когда вы сравниваете объекты через A == B, Python на самом деле вызывает A.__eq__(B).


Реализация магических методов для сравнения

Чтобы класс поддерживал сравнение, достаточно реализовать соответствующие методы. Рассмотрим пример, в котором мы создадим класс Shape и добавим в него методы для проверки на равенство == и на меньшее <.

class Shape:
    def __init__(self, area):
        self.area = area

    def __eq__(self, other):
        if isinstance(other, Shape):
            return self.area == other.area
        return False

    def __lt__(self, other):
        if isinstance(other, Shape):
            return self.area < other.area
        return False

Теперь объекты класса Shape могут сравниваться друг с другом с помощью операторов == и <. Например:

s1 = Shape(30)
s2 = Shape(45)

assert s1 == Shape(30)  # True
assert s1 < s2          # True
assert s1 == s2         # False


Пример сравнения с числами

Также можно реализовать логику, чтобы объекты сравнивались не только с другими объектами класса, но и с числами:

class Shape:
    def __init__(self, area):
        self.area = area

    def __lt__(self, other):
        if isinstance(other, Shape):
            return self.area < other.area
        elif isinstance(other, (int, float)):
            return self.area < other
        return NotImplemented

s = Shape(25)
assert s < 30  # True
assert s < 10  # False

Но если попытаться сравнить объект с числом через >, возникнет ошибка:

TypeError: '>' not supported between instances of 'Shape' and 'int'


Механизм обратного сравнения

Операции сравнения являются бинарными, то есть в них участвуют два операнда. Python вызывает метод сравнения у левого операнда. Например, при выражении A > B Python вызывает метод A.__gt__(B). Если метод __gt__ не определен в классе A, Python попробует переставить операнды и изменить оператор на противоположный, вызвав метод B.__lt__(A).

Таким образом, если в классе Shape метод __gt__ не реализован, а метод __lt__ есть, то при сравнении двух объектов s1 > s2 Python фактически выполнит операцию s2.__lt__(s1).

Однако, если мы пытаемся сравнить объект с числом через >, Python попытается вызвать other.__lt__(self). Но типы int и float не умеют сравниваться с пользовательскими объектами, поэтому возникнет ошибка:

s > 5  # TypeError: '>' not supported between instances of 'Shape' and 'int'


Как избежать дублирования кода

Для каждого из шести операторов сравнения в Python есть соответствующий магический метод. Тем не менее, реализация всех этих методов может привести к избыточности. Чтобы этого избежать, достаточно реализовать только три метода, поскольку они противоположны друг другу:

Метод сравненияПротивоположный метод
__eq____ne__
__lt____gt__
__le____ge__

Для примера реализуем методы __eq__, __lt__ и __le__. Оператор <= можно выразить через комбинацию операторов < и ==, так как <= буквально означает «меньше или равно».

class Shape:
    def __init__(self, area):
        self.area = area

    def __eq__(self, other):
        if isinstance(other, Shape):
            return self.area == other.area
        return False

    def __lt__(self, other):
        if isinstance(other, Shape):
            return self.area < other.area
        elif isinstance(other, (int, float)):
            return self.area < other
        return NotImplemented

    def __le__(self, other):
        return self == other or self < other


Полный пример с тестами

Теперь наш класс поддерживает все необходимые операторы сравнения:

s1 = Shape(30)
s2 = Shape(45)

assert s1 == Shape(30)    # True
assert s1 != s2           # True
assert s1 < s2            # True
assert s1 <= s2           # True
assert s2 > s1            # True
assert s2 >= Shape(45)    # True
assert s1 <= 35           # True
assert s2 > 40            # True

 

В этом уроке мы научились добавлять поддержку операций сравнения в пользовательские классы, используя магические методы. Мы увидели, как эффективно реализовать три из шести операторов сравнения и избежать дублирования кода.

Перейти к следующему шагу

Комментарии