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]
Отож перевантаження індексів — крута можливість, і я раджу користуватися нею на практиці. А на сьогодні — все.
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ