Python. Перегрузка операторов в классах. Общие сведения




Перегрузка операторов в классах. Общие сведения. Методы, перегружающие операторы. Примеры


Содержание


Поиск на других ресурсах:

1. Общие сведения о перегрузке операторов

В языке программирования Python, классы (в отличие от модулей) могут переопределять операторы. Такой механизм называется перегрузкой операторов. Перегрузка оператора в классе — это сопоставление оператору языка Python (например, оператору +, , * и т.д.) соответствующего метода с целью вызова кода этого метода при использовании перегруженного оператора. Иными словами, перегрузка операторов позволяет использовать объекты классов в качестве операндов в выражениях или других встроенных операциях, таких как сложение, умножение, получение среза, вывод и т.д.

Если в программе, для некоторого объекта класса, вызывается перегруженный оператор, то автоматически вызывается метод, который реализуюет действия этого оператора. Таким образом, при перегрузке оператора + (сложение), в реализующем его методе можно запрограммировать любые действия, даже вычитание, умножение или что угодно. Однако логики, такой код иметь не будет, вводя в заблуждение самого программиста. Привычным делом, метод, соответствующий оператору +, должен что-то суммировать.

Использование механизма перегрузки операторов в классах дает следующие преимущества:

  • объекты классов теснее интегрируются в объектную модель языка Python;
  • объекты классов действуют более естественно, так же как встроенные объекты языка;
  • появляется возможность использовать объекты классов в сочетании со встроенными типами;
  • программный код имеет тенденцию к лучшему восприятию.

Перегрузка операторов в классах имеет целью создание удобных инструментальных средств для других программистов, использующих эти классы в своих проектах.

 

2. Принципы, лежащие в основе перегрузки операторов в классах

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

  • при использовании перегрузки операторов в классе, используются методы, начинающиеся и заканчивающиеся двумя символами подчеркивания (например __add__(), __str__() и другие). Эти методы имеют специальное назначение и строго определенные (фиксированные) имена для каждой операции;
  • как только вызывается встроенный оператор (операция), то вызов соответствующего метода происходит автоматически. Например, при указании переопределенного оператора + для объекта класса, будет автоматически вызываться метод __add __(). Этот метод обязательно должен быть реализован в классе;
  • если нужно получить результат операции для объекта класса, то в методе класса этот результат возвращается оператором return;
  • в классах может быть переопределено большинство вложенных операторов языка Python. Каждому оператору соответствует имя метода, который реализует перегрузку;
  • если в классе не реализовано переопределение некоторого оператора, то это означает, что экземпляр (объект) этого класса не поддерживает эти операции. Например, если в классе не реализован метод __sub__(), то объект этого класса не поддерживает операцию вычитания (X-Y);
  • переопределение в классе операторов не является обязательной функциональной особенностью. Иными словами, класс не обязательно должен содержать методы переопределения операторов;
  • перегрузка операторов в классах позволяет интегрировать эти классы в объектную модель языка Python. Экземпляры классов лучше вписываются в интерфейс языка и действуют как встроенные типы.

 

3. Перечень методов, которые можно перегружать

Ниже приведен сокращенный перечень методов, которые перегружают различные операции. Методы группируются по следующим категориям:

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

 

3.1. Методы для всех видов операций

В этой категории определяются следующие методы:

  • __new__(cls [, arg]*) – вызывается для создания и возврата нового экземпляра класса;
  • __init__(self [, arg]*) – заменяет конструктор класса при создании экземпляра класса;
  • __del__(self) – заменяет деструктор при сборке экземпляра класса в «мусор»;
  • __repr__(self) – вызывается в операции repr(self);
  • __str__(self) – вызывается в операции str(self) или print(self);
  • __format__(self, format_specification) – вызывается встроенной функцией format();
  • __bytes__(self) – вызывается функцией bytes() с целью возврата строкового представления типа bytes объекта self;
  • __hash__(self) – вызывается при обработке хешированных коллекций;
  • __bool__(self) – используется при проверке значения на истинность;
  • __call__(self [, arg]*) – используется в операции self(args) при вызове экземпляра класса как функции;
  • __getattr__(self, name) – вызывается по ссылке self.name, где name — символьная строка, обозначающая доступ к неопределенному атрибуту;
  • __setattr__(self, name) – вызывается в операции self.name = value;
  • __delattr__(self, name) – вызывается в операции del self.name;
  • __lt__(self, other) – вызывается при проверке условия self<other;
  • __le__(self, other) – вызывается при проверке условия self<=other;
  • __eq__(self, other) – вызывается при проверке self==other;
  • __ne__(self, other) – вызывается при проверке self!=other;
  • __gt__(self, other) – вызывается при проверке self>other;
  • __ge__(self, other) – вызывается при проверке self>=other;
  • __slots__ – формирует дескриптор управления на уровне класса с резервированием места для объявляемых атрибутов в экземплярах классов;
  • __instancecheck__(self, instance) – возвращает True для функции isinstance(), если экземпляр является прямым или неявным экземпляром класса;
  • __subclasscheck__(self, subclass) – возвращает True для функции issubclass(), если подкласс должен считаться прямым или неявным подклассом данного класса;
  • __dir__(self) – вызывается в операции dir(self) и возвращает последовательность имен атрибутов.

 

3.2. Методы перегрузки операторов работы с коллекциями

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

  • __len__(self) – вызывается в операции len() для получения размера коллекции;
  • __contains__(self, item) – вызывается в операции item in self для проверки того, находится ли элемент item в коллекции self;
  • __iter__(self) – вызывается в операции iter(self);
  • __next__(self) – вызывается встроенной функцией next(self) в итерационном цикле;
  • __getitem__(self, key) – вызывается в операциях self[key], self[i:j:k], x in self;
  • __setitem__(self, key, value) – вызывается в операциях self[key] = value и self[i:j:k]=value;
  • __delitem__(self, key) – вызывается в операциях del self[key] и del self[i:j:k];
  • __reversed__(self) – вызывается встроенной функцией reversed() для реверсирования коллекции.

 

3.3. Методы для числовых операций в двоичной форме

Методы операций над двоичными числами делятся на подгруппы, которые описаны ниже.

3.3.1. Основные методы для операций над двоичными числами

К этой группе методов принадлежат:

  • __add__(self, other) – вызывается в операторе суммирования self + other;
  • __sub__(self, other) – вызывается в операторе вычитания self — other;
  • __mul__(self, other) – вызывается в операторе умножения self * other;
  • __truediv__(self, other) – вызывается в операторе деления self / other;
  • __floordiv__(self, other) – вызывается в операторе self // other;
  • __mod__(self, other) – вызывается в операторе self % other;
  • __divmod__(self, other) – вызывается в операторе divmod(self, other);
  • __pow__(self, other [, module]) – вызывается в операторе pow(self, other [, module]);
  • __lshift__(self, other) – вызывается в операторе сдвига влево self << other;
  • __rshift__(self, other) – вызывается в операторе сдвига вправо self >> other;
  • __and__(self, other) – вызывается в операторе self & other;
  • __xor__(self, other) – вызывается в операторе self ^ other;
  • __or__(self, other) – вызывается в операторе self | other.

 

3.3.2. Методы для правосторонних операций над двоичными числами

В данной группе методов аргумент self размещается справа от операнда. Например, если в левосторонней операции умножения

self * other

происходит вызов

self.__mul__(other)

тогда, как в правосторонней операции умножения

other * self

происходит вызов

self.__rmul__(other)

Перечень правосторонних операций над двоичными числами следующий:

  • __radd__(self, other) – аналог операции __add__(self, other) только правосторонний. Это значит, что аргумент self размещается в правой части оператора +: other + self;
  • __rsub__(self, other) – вызывается в операторе вычитания other-self;
  • __rmul__(self, other) – вызывается в операторе умножения other*self;
  • __rtruediv__(self, other) – вызывается в операторе деления other/self;
  • __rfloordiv__(self, other) – вызывается в операторе other//self;
  • __rmod__(self, other) – вызывается в операторе other % self;
  • __rdivmod__(self, other) – вызывается в операторе divmod(other, self);
  • __rpow__(self, other [, module]) – вызывается в операторе pow(other, self [, module]);
  • __rlshift__(self, other) – вызывается в операторе сдвига влево other << self;
  • __rrshift__(self, other) – вызывается в операторе сдвига вправо other >> self;
  • __rand__(self, other) – вызывается в операторе other & self;
  • __rxor__(self, other) – вызывается в операторе other ^ self;
  • __ror__(self, other) – вызывается в операторе other | self.

 

3.3.3. Комбинированные методы для операций над двоичными числами

Это методы, которые используются при перегрузке операций вроде +=, -=, >>= и других. Перечень этих методов следующий:

  • __iadd__(self, other) – вызывается в операторе +=;
  • __isub__(self, other) – вызывается в операторе -=;
  • __imul__(self, other) – вызывается в операторе *=;
  • __itruediv__(self, other) – вызывается в операторе деления /=;
  • __ifloordiv__(self, other) – вызывается в операторе //=;
  • __imod__(self, other) – вызывается в операторе %=;
  • __ipow__(self, other [, modulo]) – вызов **=;
  • __ilshift__(self, other) – вызывается в операторе <<=;
  • __irshift__(self, other) – вызов >>=;
  • __iand__(self, other) – вызов &=;
  • __ixor__(self, other) – вызов ^=;
  • __ior__(self, other) – вызов |=.

 

3.4. Методы для других операций над числами

К этой группе методов относятся следующие:

  • __neg__(self) – вызывается в операции -self;
  • __pos__(self) – вызывается в операции +self;
  • __abs__(self) – вызывается в операции abs(self);
  • __invert__(self) – вызов ~self;
  • __complex__(self) – вызов в операции complex(self);
  • __int__(self) – вызов в операции int(self);
  • __float__(self) – вызов в операции float(self);
  • __round__(self) – вызов round(self);
  • __index__(self) – вызов index(self).

 

3.5. Методы для операций с дескрипторами

Эта группа методов применяется в случаях, когда атрибуту класса-владельца присваивается экземпляр класса (дескриптор), в котором эти методы определены. Перечень этих методов следующий:

  • __get__(self, instance, owner) – вызывается для получения атрибута из класса-владельца (owner) или экземпляра данного класса (instance);
  • __set__(self, instance, value) – вызывается для установления нового значения атрибута в экземпляре класса-владельца;
  • __delete__(self, instance) – вызывается когда нужно удалить атрибут в экземпляре класса-владельца.

 

3.6. Методы для операций, используемых с диспетчерами контекста

К этим методам относятся методы, реализующие протокол диспетчера контекста. Этот протокол используется в операторе with. Перечень этих методов следующий:

  • __enter__(self) – предназначен для входа в динамический контекст, связанный с данным объектом;
  • __exit__(self) – реализует выход из динамического контекста связанного с данным объектом.

 

4. Перегрузка конструктора класса. Пример

Когда создается экземпляр из класса, тогда первым методом вызывается конструктор этого класса. В этот метод-конструктор целесообразно вписать код начальной инициализации данных класса, выделения необходимых ресурсов и тому подобное. Чтобы создать конструктор в классе, в этот класс должен быть включен метод __init __().

Примерный код класса, содержащий конструктор следующий

class ClassName:
    # конструктор, получающий некоторые параметры ...
    def __init__(self, ...):
        # инициализация в конструкторе
        ...
...

После такой реализации класса, можно создавать экземпляр класса вызовом

obj = ClassName(...) # здесь ... - список параметров конструктора

Пример. Объявляется класс Book, который описывает следующие данные о книге:

  • author — имя автора книги;
  • title — название книги;
  • year — год издания;
  • price — стоимость книги.

В классе Book объявляются следующие методы:

  • метод __init __() — реализует конструктор класса, который получает 4 параметра;
  • метод display() — выводит данные о книге в удобном виде;
  • методы доступа к внутренним данным книги: getAuthor(), getTitle(), getYear(), getPrice().

После объявления класса демонстрируется создание экземпляра книги с помощью метода-конструктора.

# Перегрузка операторов в Python.
# Перегрузка конструктора путем
# реализации метода __init__() в классе
class Book:
    # Метод-конструктор
    def __init__(self, author, title, year, price):
        self.author = author
        self.title = title
        self.year = year
        self.price = price

    # Метод, выводящий данные о книге
    def display(self):
        print("author = ", self.author, ", title = ", self.title,
              ", year = ", self.year, ", price = ", self.price)

    # Методы доступа к данным в классе
    def getAuthor(self):
        return self.author

    def getTitle(self):
        return self.title

    def getYear(self):
        return self.year

    def getPrice():
        return self.price

# 1. Создать объект класса Book,
#   вызывается метод-конструктор __init__()
book1 = Book("Mark Lutz", "Python. ", 2015, 100.01)

# 2. Отобразить книгу на экране
book1.display()

Результат выполнения программы

author = Mark Lutz , title =   Python. , year =   2015 , price = 100.01

 

5. Перегрузка оператора + для класса Point (точка). Пример

Если в классе нужно перегрузить оператор +, то в этом классе нужно реализовать метод __add __(). Общий вид реализации метода __add __() следующий:

class ClassName:
    def __add__(self, value):
        # действия, выполняющие суммирование
        # ...
        return result # вернуть некоторый результат

После этого можно вызвать метод __add __() как обычную операцию сложения. Например, если нужно добавить два экземпляра класса ClassName, то код будет примерно таким

obj1 = ClassName()
obj2 = ClassName()
...
obj3 = obj1 + obj2 # заменяется на obj3 = obj1.__add__(obj2)
...

Пример. В примере объявляется класс Point, который реализует точку на координатной плоскости. В классе реализованы следующие методы:

  • метод __init __(), который перегружает конструктор класса. Конструктор класса вызывается при создании экземпляра класса;
  • метод getXY(), который возвращает значение координаты точки (x, y) в виде списка;
  • метод __add __() — реализует перегрузку оператора сложения +. Реализация предусматривает суммирование координат по осям X и Y;
  • метод Show() — выводит координаты точки на экран.

Ниже приведен текст демонстрационной программы.

# Перегрузка операторов в Python.
# Перегрузка оператора (операции) + (сложение).

# Класс, который содержит метод __add__()
class Point:
    # Метод инициализации - конструктор
    def __init__(self, x, y):
        self.x = x
        self.y = y

    # Метод, который возвращает (x, y) в виде списка
    def getXY(self):
        return [ self.x, self.y ]

    # Метод, который перегружает оператор +
    def __add__(self, point):
        self.x = self.x + point.x
        self.y = self.y + point.y
        return self

    # Метод, который выводит точку
    def Show(self, text):
        print(text, " = (", self.x, "; ", self.y, ")")

# Создать точку 1
pt1 = Point(5, 6)
pt1.Show("pt1")

# Создать точку 2
pt2 = Point(7, 11)
pt2.Show("pt2")

# Сложить две точки - использовать перегрузку оператора
pt3 = pt1 + pt2 # вызов метода __add__()
pt3.Show("pt3 = pt1 + pt2")

После запуска на выполнение программа выдаст следующий результат

pt1 = ( 5 ; 6 )
pt2 = ( 7 ; 11 )
pt3 = pt1 + pt2 = ( 12 ; 17 )

 


Связанные темы