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

Как мы знаем, оператор + может выполнять сложение двух чисел, объединять два списка или конкатенировать две строки.

С некоторыми изменениями мы можем использовать оператор + для работы и с пользовательскими объектами. Эта возможность в Python, которая позволяет одному и тому же оператору иметь разные значения в зависимости от контекста, называется перегрузкой операторов.


Специальные функции в Python

В Python методы, которые имеют два подчёркивания __ до и после своих имён, имеют особое значение. Например, __add__(), __len__() и т.д.

Эти специальные методы могут использоваться для реализации определённых функций или поведения.

Давайте используем метод __add__() для сложения двух чисел вместо использования оператора +.

number1 = 5

# similar to number2 = number1 + 6
number2 = number1.__add__(6)

print(number2)  # 11

Возможно использовать метод __add__() для целых чисел, потому что:

  • Всё в Python — это объект, включая целые числа.

  • У целых чисел определён метод __add__(), который мы можем использовать.

На самом деле оператор + внутренне вызывает метод __add__() для сложения целых чисел и чисел с плавающей точкой.

Вот некоторые из специальных функций, доступных в Python:

Функция

Описание

__init__()

Инициализирует атрибуты объекта.

__str__()

Возвращает строковое представление объекта.

__len__()

Возвращает длину объекта.

__add__()

Складывает два объекта.

__call__()

Вызывает объекты класса как обычную функцию.


Как использовать перегрузку операторов?

Предположим, мы хотим использовать оператор + для сложения двух пользовательских объектов.

Поскольку оператор + внутренне вызывает метод __add__(), если мы реализуем этот метод в классе, мы можем заставить объекты этого класса работать с оператором +.

Пример: Сложение двух координат (без перегрузки)

Сначала давайте напишем программу для сложения двух координат (без использования перегрузки оператора +).

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

    def add_points(self, other):
        x = self.x + other.x
        y = self.y + other.y
        return Point(x, y)


p1 = Point(1, 2)
p2 = Point(2, 3)

p3 = p1.add_points(p2)

print((p3.x, p3.y))   # Output: (3, 5)

В приведённом выше примере мы создали метод add_points() для сложения двух точек. Для вызова этого метода мы использовали p1.add_points(p2).

Давайте напишем тот же код, используя оператор + для сложения двух точек.

Пример: Сложение двух координат (с перегрузкой)

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

    def __add__(self, other):
        x = self.x + other.x
        y = self.y + other.y
        return Point(x, y)


p1 = Point(1, 2)
p2 = Point(2, 3)

# this statment calls the __add__() method
p3 = p1 + p2

print((p3.x, p3.y))   # Output: (3, 5)

Здесь этот код p1 + p2 вызывает метод __add__(self, other). Параметр self принимает p1, а параметр other принимает p2 в качестве аргументов.

Не злоупотребляйте операторами

В приведённой программе мы могли бы легко использовать оператор + для вычитания следующим образом:

def __add__(self, other):
    x = self.x - other.x
    y = self.y - other.y
    return Point(x, y)

Теперь оператор + в приведённом коде выполняет вычитание точек. Хотя программа работает без ошибок, вам следует абсолютно избегать этого. Мы всегда должны использовать операторы соответствующим образом при их перегрузке.

Аналогично, мы можем перегрузить и другие операторы. Специальные функции, которые нам нужно реализовать, приведены в таблице ниже.

Оператор

Выражение

Внутренне

Сложение

p1 + p2

p1.__add__(p2)

Вычитание

p1 - p2

p1.__sub__(p2)

Умножение

p1 * p2

p1.__mul__(p2)

Степень

p1 ** p2

p1.__pow__(p2)

Деление

p1 / p2

p1.__truediv__(p2)

Целочисленное деление

p1 // p2

p1.__floordiv__(p2)

Остаток (по модулю)

p1 % p2

p1.__mod__(p2)

Побитовый сдвиг влево

p1 << p2

p1.__lshift__(p2)

Побитовый сдвиг вправо

p1 >> p2

p1.__rshift__(p2)

Побитовое AND

p1 & p2

p1.__and__(p2)

Побитовое OR

p1 | p2

p1.__or__(p2)

Побитовое XOR

p1 ^ p2

p1.__xor__(p2)

Побитовое NOT

~p1

p1.__invert__()


Перегрузка операторов сравнения

Python не ограничивает перегрузку операторов арифметическими операторами. Мы также можем перегружать операторы сравнения.

Вот пример того, как мы можем перегрузить оператор < для сравнения двух объектов класса Person на основе их age:

class Person:
    def __init__(self, name, age):
        self.name = name
        self.age = age

    # overload < operator
    def __lt__(self, other):
        return self.age < other.age

p1 = Person("Alice", 20)
p2 = Person("Bob", 30)

print(p1 < p2)  # prints True
print(p2 < p1)  # prints False

Вывод

True
False

Здесь __lt__() перегружает оператор < для сравнения атрибута age двух объектов.

Метод __lt__() возвращает:

  • True — если возраст первого объекта меньше возраста второго объекта

  • False — если возраст первого объекта больше возраста второго объекта

Аналогично, специальные функции, которые нам нужно реализовать для перегрузки других операторов сравнения, приведены в таблице ниже.

Оператор

Выражение

Внутренне

Меньше чем

p1 < p2

p1.__lt__(p2)

Меньше или равно

p1 <= p2

p1.__le__(p2)

Равно

p1 == p2

p1.__eq__(p2)

Не равно

p1 != p2

p1.__ne__(p2)

Больше чем

p1 > p2

p1.__gt__(p2)

Больше или равно

p1 >= p2

p1.__ge__(p2)


Преимущества перегрузки операторов

Вот некоторые преимущества перегрузки операторов:

  • Улучшает читаемость кода, позволяя использовать знакомые операторы.

  • Гарантирует, что объекты класса ведут себя согласованно со встроенными типами и другими пользовательскими типами.

  • Упрощает написание кода, особенно для сложных типов данных.

  • Позволяет повторно использовать код, реализуя один метод оператора и используя его для других операторов.


Смотрите также: