Перегрузка операторов в Python — методы и примеры

В этом руководстве вы узнаете, как перегружать операторы в Python и использовать их, чтобы ваши объекты работали со встроенными операторами.

Содержание

Что такое перегрузка операторов в Python?

Предположим, у вас есть класс Point2D с атрибутами координат x и y:

class Point2D:
    def __init__(self, x, y):
        self.x = x
        self.y = y

    def __str__(self):
        return f'({self.x},{self.y})'

Чтобы добавить два объекта Point2D, вы можете определить метод add() следующим образом:

class Point2D:
    def __init__(self, x, y):
        self.x = x
        self.y = y

    def __str__(self):
        return f'({self.x},{self.y})'

    def add(self, point):
        if not isinstance(point, Point2D):
            raise ValueError('The other must be an instance of the Point2D')

        return Point2D(self.x + point.x, self.y + point.y)

Метод add() выдает ошибку, если точка не является экземпляром класса Point2D. В противном случае он возвращает новый объект Point2D, координаты x и y которого являются суммами координат x и y двух точек.

Следующий пример создает два экземпляра класса Point2D и использует метод add() для добавления двух точек:

a = Point2D(10, 20)
b = Point2D(15, 25)
c = a.add(b)

print(c)

Выход:

(25,45)

Этот код работает отлично. Но у Python есть лучший способ реализовать это. Вместо использования метода add() вы можете использовать встроенный оператор(+) следующим образом:

c = a + b

Когда вы используете оператор + для объекта Point2D, Python вызывает для этого объекта специальный метод __add__(). Следующие вызовы эквивалентны:

c = a + b
c = a.__add__(b)

Метод __add__() должен возвращать новый экземпляр объекта Point2D. Возможность использовать встроенный оператор(+) для пользовательского типа называется перегрузкой оператора.

Ниже показан класс Point2D, реализующий специальный оператор __add__() для поддержки оператора +:

class Point2D:
    def __init__(self, x, y):
        self.x = x
        self.y = y

    def __str__(self):
        return f'({self.x},{self.y})'

    def __add__(self, point):
        if not isinstance(point, Point2D):
            raise ValueError('The other must be an instance of the Point2D')

        return Point2D(self.x + point.x, self.y + point.y)


if __name__ == '__main__':
    a = Point2D(10, 20)
    b = Point2D(15, 25)
    c = a + b
    print(c)

Выход:

(25,45)

Специальные методы для перегрузки операторов

Ниже показаны операторы с соответствующими специальными методами:

Оператор Специальные методы
+ __add__(self, other)
__sub__(self, other)
* __mul__(self, other)
/ __trediv__(self, other)
// __floordiv__(self, other)
% __mod__(self, other)
** __pow__(self, other)
>> __rshift__(self, other)
<< __lshift__(self, other)
& __and__(self, other)
| __or__(self, other)
^ __xor__(self, other)

Например, вы можете реализовать метод __sub__() в Point2D для поддержки вычитания(-) двух точек:

class Point2D:
    def __init__(self, x, y):
        self.x = x
        self.y = y

    def __str__(self):
        return f'({self.x},{self.y})'

    def __add__(self, point):
        if not isinstance(point, Point2D):
            raise ValueError('The other must be an instance of the Point2D')

        return Point2D(self.x + point.x, self.y + point.y)

    def __sub__(self, other):
        if not isinstance(other, Point2D):
            raise ValueError('The other must be an instance of the Point2D')

        return Point2D(self.x - other.x, self.y - other.y)


if __name__ == '__main__':
    a = Point2D(10, 20)
    b = Point2D(15, 25)
    c = b - a
    print(c)

Перегрузка встроенных операторов

У некоторых операторов есть встроенная версия. Например, встроенная версия + — это +=.

  • Для неизменяемых типов, таких как кортеж, строка, число, операторы inplace выполняют вычисления и не присваивают результат обратно входному объекту.
  • Для изменяемого типа встроенный оператор выполняет обновления непосредственно исходных объектов. Задание не является обязательным.

Python также предоставляет список специальных методов, позволяющих перегрузить inplace-операторы:

Оператор Специальный метод
+= __iadd__(self, other)
«=» __isub__(self, other)
«=» __imul__(self, other)
«=» __itruediv__(self, other)
«=» __ifloordiv__(self, other)
«=» __imod__(self, other)
«=» __ipow__(self, other)
>>= __irshift__(self, other)
<<= __ilshift__(self, other)
«=» __iand__(self, other)
|= __ior__(self, other)
^= __ixor__(self, other)

Давайте рассмотрим пример перегрузки оператора +=.

Предположим, у вас есть объект корзины и вы хотите добавить в корзину товар. Для этого вы можете определить метод add() для класса Cart и использовать его следующим образом:

cart.add(item)

Альтернативно вы можете реализовать оператор += в классе Cart. Он позволяет добавить товар в корзину следующим образом:

cart += item

Для поддержки оператора += необходимо реализовать специальный метод __iadd__ в классе Cart.

  • Сначала определите класс Item, который имеет три атрибута: имя (name), количество (quantity) и цену (price). Кроме того, у него есть свойство суммы, которое возвращает промежуточную сумму элемента:
class Item:
    def __init__(self, name, qty, price):
        self.name = name
        self.qty = qty
        self.price = price

    @property
    def amount(self):
        return self.qty * self.price

    def __str__(self):
        return f'{self.name} {self.qty} ${self.price} ${self.amount}'
  • Во-вторых, определите класс Cart, реализующий метод __iadd__:
class Cart:
    def __init__(self):
        self.items = []

    def __iadd__(self, item):
        if not isinstance(item, Item):
            raise ValueError('The item must be an instance of Item')

        self.items.append(item)
        return self

    @property
    def total(self):
        return sum([item.amount for item in self.items])

    def __str__(self):
        if not self.items:
            return 'The cart is empty'

        return '\n'.join([str(item) for item in self.items])

В методе __iadd__ мы вызываем ValueError, если элемент не является экземпляром класса Item. В противном случае мы добавляем элемент в атрибут списка элементов. Свойство Total возвращает сумму всех элементов.

Метод __str__ возвращает строку «The cart is empty», если в корзине нет товара. В противном случае возвращается строка, содержащая все элементы, разделенные новой строкой.

  • В-третьих, используйте оператор +=, чтобы добавить товар в корзину:
if __name__ == '__main__':
    cart = Cart()

    cart += Item('Apple', 5, 2)
    cart += Item('Banana', 20, 1)
    cart += Item('Orange', 10, 1.5)

    print(cart)
    # print the total line
    print('-' * 30)
    print('Total: $', cart.total)

Выход:

Apple   5       $2      $10
Banana  20      $1      $20
Orange  10      $1.5    $15.0
------------------------------
Total: $ 45.0
Похожие посты
Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *