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, 50.01)

# 2. Відобразити книгу на екрані
book1.display()

Результат роботи програми

author = Mark Lutz , title =   Python. , year =   2015 , price = 50.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 )

 


Теми, близькі за змістом