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