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