JavaRush/Курси/Модуль 1: Python Core/Перевантаження операторів

Перевантаження операторів

Відкрита

6.1 Магічні методи

Перевантаження операторів в Python дозволяє визначити або змінити поведінку вбудованих операторів (наприклад, +, -, *, /) для користувацьких класів. Це робиться за допомогою спеціальних методів, котрі також називаються магічними методами.

Наприклад, у своєму класі ви можете перевантажити оператори порівняння:

Оператор Метод без підкреслення Сигнатура метода
== eq() __eq__(self, other)
!= ne() __ne__(self, other)
< lt() __lt__(self, other)
<= le() __le__(self, other)
> gt() __gt__(self, other)
>= ge() __ge__(self, other)

Припустимо, ви написали свій клас і хочете, щоб об'єкти вашого класу порівнювались саме так, як вам це потрібно. Вам потрібно просто реалізувати в вашому класі метод «__eq__», і Python буде викликати його кожного разу, коли в коді порівнюються об'єкти вашого класу.

Приклад:

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

    def __eq__(self, other):
        return self.x == other.x and self.y == other.y

# Використання
v1 = Vector(2, 3)
v2 = Vector(2, 3)
v3 = Vector(4, 5)
print(v1 == v2)  # Виведе: True
print(v1 == v3)  # Виведе: False

Кожного разу, коли ви порівнюєте два ваші об'єкти, Python перевіряє, а чи є у них реалізована функція «__eq__». Якщо є, то викликає її. А якщо її немає, то просто буде порівнювати посилання на об'єкти.

Фактично в прикладі вище написано (тільки ще перевірку на наявність метода треба додати):

# Використання
v1 = Vector(2, 3)
v2 = Vector(2, 3)
v3 = Vector(4, 5)
print(v1.__eq__(v2))  # Виведе: True
print(v1.__eq__(v3))  # Виведе: False

6.2 Список всіх операторів

Всього для перевантаження доступно 6 груп операторів.

Арифметичні оператори:

Оператор Метод без підкреслення Сигнатура метода
+ add __add__(self, other)
- sub __sub__(self, other)
* mul __mul__(self, other)
/ truediv __truediv__(self, other)
// floordiv __floordiv__(self, other)
% mod __mod__(self, other)
** pow __pow__(self, other)

Оператори порівняння:

Оператор Метод без підкреслення Сигнатура метода
== eq __eq__(self, other)
!= ne __ne__(self, other)
< lt __lt__(self, other)
<= le __le__(self, other)
> gt __gt__(self, other)
>= ge __ge__(self, other)

Логічні оператори:

Оператор Метод без підкреслення Сигнатура метода
& and __and__(self, other)
| or __or__(self, other)
^ xor __xor__(self, other)
~ invert __invert__(self)

Оператори індексації та зрізів:

Оператор Метод
obj[key] __getitem__(self, key)
obj[key] = value __setitem__(self, key, value)
del obj[key] __delitem__(self, key)

Унарні оператори:

Оператор Метод
- __neg__(self)
+ __pos__(self)
abs() __abs__(self)
~ __invert__(self)

Оператори присвоєння:

Оператор Метод
+= __iadd__(self, other)
-= __isub__(self, other)
*= __imul__(self, other)
/= __itruediv__(self, other)
//= __ifloordiv__(self, other)
%= __imod__(self, other)
**= __ipow__(self, other)

Можливо, саме тому Python такий повільний – кожного разу перед виконанням оператора він шукає аналогічну функцію у класу та всіх його класів-батьків. Зате це дозволяє писати найкомпактніший код у світі :)

6.3 Оператор індексації

Те, що можна порівнювати об'єкти або вичитувати множини – це в якомусь сенсі очевидно. І ви самі здогадаєтесь про це, якщо будете писати клас, що передбачає над ним логічні чи математичні операції.

Хочу з вами розглянути такий цікавий приклад – як оператор індексації. Давайте одразу почнемо з коду прикладу:

class CustomList:
    def __init__(self, data):
        self.data = data

    def __getitem__(self, index):
        return self.data[index]

    def __setitem__(self, index, value):
        self.data[index] = value

    def __delitem__(self, index):
        del self.data[index]

    def __repr__(self):
        return repr(self.data)

# Використання
c_list = CustomList([1, 2, 3, 4, 5])
print(c_list[1])  # Виведе: 2
c_list[1] = 10
print(c_list)  # Виведе: [1, 10, 3, 4, 5]
del c_list[1]
print(c_list)  # Виведе: [1, 3, 4, 5]

Тут ми бачимо приклад трьох операцій:

  • Читання даних через індекс.
  • Запис даних через індекс.
  • І навіть видалення даних через індекс.

А взагалі не обов'язково, що дані всередині будуть зберігатися у вигляді списку. Або індекс не обов'язково має бути числом. Наприклад, клас dictionary (словник) саме так і реалізований.

Створюємо SuperList

Пам'ятаєте клас list? Йому можна присвоювати елементи, але тільки з тими індексами, що вже існують. Давайте зробимо свій клас, назвемо його SuperList, до елементів якого можна буде звертатися по будь-якому індексу:

  • Якщо індекс < 0, то будемо вставляти елемент на початок.
  • Якщо індекс >= len, то будемо додавати елемент в кінець.
  • В інших випадках просто повертати елемент.

Приклад:

class SuperList(list):
    def __init__(self, value):
        super().__init__(value)

    def __setitem__(self, index, value):
        if index >= len(self):
            super().append(value)
        elif index < 0:
            super().insert(0, value)
        else:
            super().__setitem__(index, value)

lst = SuperList([1, 2, 3])
lst[200] = 100
lst[-200] = 99
print(lst)  # [99, 1, 2, 3, 100]

Отож перевантаження індексів — крута можливість, і я раджу користуватися нею на практиці. А на сьогодні — все.

2
Задача
Модуль 1: Python Core,  11 рівень,  5 лекція
Недоступна
Перевантаження операторів порівняння
Перевантаження операторів порівняння
2
Задача
Модуль 1: Python Core,  11 рівень,  5 лекція
Недоступна
Перевантаження операторів індексації
Перевантаження операторів індексації
Коментарі (2)
  • популярні
  • нові
  • старі
Щоб залишити коментар, потрібно ввійти в систему
Felton Працює в НГУ
14 серпня, 12:16
# Перевантаження операторів індексації # Напишіть клас Matrix, який буде представляти двовимірну матрицю # та підтримувати перевантаження операторів індексації ([]). # Реалізуйте методи __getitem__ та __setitem__. class Matrix: # Напишіть тут ваш код def __init__(self, cols, rows): self.cols = cols self.rows = rows self.matrix = [[0 for _ in range(self.cols)] for _ in range(self.rows)] def _normalize_index(self, index): """Перевіряє межі і перетворює негативні індекси у звичайні.""" row, col = index if not (-self.rows <= row < self.rows): raise IndexError('Індекс рядка виходить за рамки діапазону') if not (-self.cols <= col < self.cols): raise IndexError('Індекс стовпця виходить за рамки діапазону') # перетворюємо негативний індекс на позитивний if row < 0: row += self.rows if col < 0: col += self.cols return row, col def __getitem__(self, index): row, col = self._normalize_index(index) return self.matrix[row][col] def __setitem__(self, index, value): row, col = self._normalize_index(index) self.matrix[row][col] = value matrix = Matrix(3, 3) matrix[0, 0] = 1 matrix[-1, -1] = 9 # останній рядок, останній стовпець print(matrix[0, 0]) # 1 print(matrix[-1, -1]) # 9 print(matrix[2, 2]) # 9 — те саме, що [-1, -1]
Andriy Trubchanin
Рівень 23
2 березня 2025, 09:11
Лекція "Перевантаження операторів" в розділі "Стандартні помилки". Структура курсу поїхала.