Перегрузка операторов в Python — методы и примеры
В этом руководстве вы узнаете, как перегружать операторы в 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