Перегрузка операторов в Python: практическое руководство
Перегрузка операторов в Python
Узнайте, как переопределять поведение операторов в Python с помощью специальных методов вроде __add__, __lt__ и других, улучшая ваши ООП-дизайны.
Перегрузка операторов в Python
Встроенные операторы Python работают по-разному в зависимости от типов операндов. Оператор +, например, складывает числа, конкатенирует строки или объединяет списки. Эта гибкость достигается через перегрузку операторов — возможность присваивать контекстно-зависимые значения операторам для ваших собственных классов.
Рассмотрим простой класс Point, представляющий координату в 2D-плоскости. Без специальных методов попытка сложить две точки вызовет TypeError:
class Point:
def __init__(self, x=0, y=0):
self.x = x
self.y = y
p1 = Point(1, 2)
p2 = Point(2, 3)
print(p1 + p2)
Вывод
Traceback (most recent call last): File "<string>", line 9, in <module> print(p1+p2) TypeError: unsupported operand type(s) for +: 'Point' and 'Point'
Поскольку Python не знает, как комбинировать два экземпляра Point, он выбрасывает исключение. Перегрузка операторов позволяет нам определить это поведение самим.
Специальные методы Python
Методы, окружённые двойными подчёркиваниями — __init__, __str__, __add__ и т.д. — называются специальными методами или «dunder»-методами. Они вызываются неявно средой выполнения Python и являются точками перехвата для перегрузки операторов, протоколов контейнеров и многого другого.
Например, метод __str__ управляет тем, как объект преобразуется в человекочитаемую строку. По умолчанию печать объекта показывает его адрес в памяти, что часто бесполезно. Переопределение __str__ делает отладку и логирование намного понятнее:
class Point:
def __init__(self, x=0, y=0):
self.x = x
self.y = y
def __str__(self):
return '({0}, {1})'.format(self.x, self.y)
Теперь при печати Point мы получаем:
p1 = Point(2, 3)
print(p1)
Вывод
(2, 3)
То же представление используется str(p1) и format(p1), так как Python внутренне вызывает p1.__str__().
Перегрузка оператора +
Чтобы настроить сложение для объектов Point, реализуйте __add__:
class Point:
def __init__(self, x=0, y=0):
self.x = x
self.y = y
def __str__(self):
return '({0}, {1})'.format(self.x, self.y)
def __add__(self, other):
return Point(self.x + other.x, self.y + other.y)
Проверка нового поведения:
p1 = Point(1, 2)
p2 = Point(2, 3)
print(p1 + p2)
Вывод
(3, 5)
Под капотом Python выполняет p1.__add__(p2), возвращая новый Point со сложенными координатами.
Ниже — краткий справочник для других арифметических операторов и соответствующих специальных методов:
| Оператор | Выражение | Специальный метод |
|---|---|---|
| Сложение | p1 + p2 | __add__(self, other) |
| Вычитание | p1 - p2 | __sub__(self, other) |
| Умножение | p1 * p2 | __mul__(self, other) |
| Возведение в степень | p1 ** p2 | __pow__(self, other) |
| Деление | p1 / p2 | __truediv__(self, other) |
| Целочисленное деление | p1 // p2 | __floordiv__(self, other) |
| Остаток от деления | p1 % p2 | __mod__(self, other) |
| Левый сдвиг | p1 << p2 | __lshift__(self, other) |
| Правый сдвиг | p1 >> p2 | __rshift__(self, other) |
| Побитовое И | p1 & p2 | __and__(self, other) |
| Побитовое ИЛИ | p1 | p2 | __or__(self, other) |
| Побитовое XOR | p1 ^ p2 | __xor__(self, other) |
| Побитовое НЕ | ~p1 | __invert__(self) |
Перегрузка операторов сравнения
Перегрузка операторов не ограничивается арифметикой. Операторы сравнения также можно адаптировать. Например, для сравнения точек по расстоянию от начала координат переопределите __lt__:
class Point:
def __init__(self, x=0, y=0):
self.x = x
self.y = y
def __str__(self):
return '({0}, {1})'.format(self.x, self.y)
def __lt__(self, other):
self_mag = self.x**2 + self.y**2
other_mag = other.x**2 + other.y**2
return self_mag < other_mag
Использование:
p1 = Point(1, 1)
p2 = Point(-2, -3)
p3 = Point(1, -1)
print(p1 < p2) # True
print(p2 < p3) # False
print(p1 < p3) # False
Остальные методы сравнения следуют схожему шаблону:
| Оператор | Выражение | Специальный метод |
|---|---|---|
| Меньше | p1 < p2 | __lt__(self, other) |
| Меньше или равно | p1 <= p2 | __le__(self, other) |
| Равно | p1 == p2 | __eq__(self, other) |
| Не равно | p1 != p2 | __ne__(self, other) |
| Больше | p1 > p2 | __gt__(self, other) |
| Больше или равно | p1 >= p2 | __ge__(self, other) |
Тщательно определяя эти методы, вы можете заставить пользовательские объекты естественно участвовать в арифметических и сравнительных контекстах, тем самым повышая читаемость и поддерживаемость кода.
Python
- Освоение типов данных в Python: практическое руководство
- Мастерство операторов Python: Полное руководство
- Оператор pass в Python: практическое руководство
- Аргументы функций в Python: позиционные, именованные и параметры по умолчанию
- Словари в Python: создание, манипуляции и продвинутые техники
- Наследование в Python: концепции, синтаксис и практические примеры
- Итераторы в Python: как они работают и как создать свои
- Замыкания в Python: как вложенные функции захватывают переменные
- Декоратор @property в Python: чистые, обратно совместимые геттеры и сеттеры
- Модуль datetime в Python: практическое руководство по датам, времени и часовым поясам