Перегрузка операторов: реализация собственного поведения через дандер-методы

Почему перегрузка операторов — это мощный инструмент? 🔥

В Python операторы (+, -, *, == и др.) работают не сами по себе — их поведение определяется специальными методами класса, которые называются магическими или дандер-методами (от англ. "double underscore"). Перегрузка операторов позволяет задать логику их работы для ваших собственных объектов.

Это как научить робота готовить кофе: вы не меняете сам кофеварку, а просто программируете новые действия!


Основные дандер-методы для перегрузки операторов

Каждый оператор в Python соответствует определенному дандер-методу. Вот самые популярные:

Оператор Дандер-метод Пример вызова
+ __add__ obj1 + obj2
- __sub__ obj1 - obj2
* __mul__ obj1 * obj2
/ __truediv__ obj1 / obj2
== __eq__ obj1 == obj2
!= __ne__ obj1 != obj2
< __lt__ obj1 < obj2
> __gt__ obj1 > obj2
str() __str__ print(obj1)

Пример: Вектора в двумерном пространстве 📐

Допустим, мы хотим создать класс Vector2D, чтобы удобно работать с двумерными векторами.

Реализация базового класса

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

Перегрузка оператора + (__add__)

Теперь научим вектора складываться:

def __add__(self, other):
    if isinstance(other, Vector2D):
        return Vector2D(self.x + other.x, self.y + other.y)
    raise TypeError("Можно складывать только Vector2D!")

Теперь работает:

v1 = Vector2D(2, 3)
v2 = Vector2D(1, 4)
result = v1 + v2  # Vector2D(3, 7)

Перегрузка оператора * (__mul__)

Умножение вектора на число:

def __mul__(self, scalar):
    if isinstance(scalar, (int, float)):
        return Vector2D(self.x * scalar, self.y * scalar)
    raise TypeError("Можно умножать только на число!")

Пример:

v = Vector2D(1, 2)
scaled = v * 3  # Vector2D(3, 6)

Перегрузка оператора == (__eq__)

Сравнение векторов:

def __eq__(self, other):
    if isinstance(other, Vector2D):
        return self.x == other.x and self.y == other.y
    return False

Теперь v1 == v2 вернет True только если координаты совпадают.

Перегрузка __str__ для красивого вывода

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

Теперь print(v1) выведет "Vector2D(2, 3)" вместо <__main__.Vector2D object at 0x...>.


Где ещё применяется перегрузка операторов? 🧠

  1. Математические объекты: матрицы, комплексные числа.
  2. Работа с деньгами: класс Money с перегруженными + и -.
  3. Кастомные коллекции: например, переопределение [] (__getitem__).
  4. Контекстные менеджеры: with использует __enter__ и __exit__.

Осторожно: подводные камни ⚠️

  1. Типы операндов. Всегда проверяйте isinstance() в методах, чтобы избежать ошибок.
  2. Изменяемые объекты. Если ваш класс mutable, убедитесь, что операции вроде += (__iadd__) обрабатываются корректно.
  3. Логика операций. Не нарушайте ожидаемое поведение: например, __eq__ должен быть симметричным (a == bb == a).

Чеклист для перегрузки операторов ✅

  1. Определите, какие операции нужны вашему классу.
  2. Реализуйте соответствующие дандер-методы.
  3. Добавьте проверки типов в методах.
  4. Протестируйте все возможные сценарии использования.

Теперь вы готовы творить магию Python! ✨

Скрыть рекламу навсегда

🎥 YouTube: программирование простым языком

Канал, где я спокойно и по шагам объясняю сложные темы — без заумных терминов и лишней теории.

Подходит, если раньше «не заходило», но хочется наконец понять.

▶️ Смотреть курсы на YouTube