Добрый день, уважаемые читатели. Эта статья является продолжением статьи про магию в Питоне. Магические методы — очень гибкий инструмент разработки пользовательских классов, и сегодня мы дополним наш класс вектора, который мы начали разрабатывать в прошлой части.
Подпишись на группу Вконтакте и Телеграм-канал. Там еще больше полезного контента для программистов.
А на YouTube-канале ты найдешь обучающие видео по программированию. Подписывайся!
Для начала разберём изменения, которые я внёс без вашего ведома.
Изменения
Я импортировал класс Iterable
(позже нам это понадобится):
from collections.abc import Iterable
Также в методе __init__()
я изменил строчку:
self.__values = list(args[0])
Теперь отправимся на путь индексации нашего объекта.
Индексация
Первый метод, который мы реализуем — __getitem__()
.
Конструкция object[key]
равнозначна object.__getitem__(key)
.
Идея этого метода в нашем классе — создать возможность индексирования по итерируемому объекту. К примеру, конструкция Vector(1, 2, 3)[[0, 2]]
должна возвращать Vector(1, 3)
. Именно для этого мы и будем использовать класс Iterable
.
def __getitem__(self, key): if isinstance(key, slice): return Vector(self.__values[key]) elif isinstance(key, Iterable): return Vector([self.__values[i] for i in key]) else: return self.__values[key]
Таким образом, метод индексации возвращает либо элемент вектора, либо другой экземпляр класса Vector
.
Конструкция object[key] = value
равноцена конструкции object.__setitem__(key, value)
.
def __setitem__(self, key, value): self.__values[key] = value
Как вы могли заметить, мы просто проводим такую же операцию со списком значений вектора и не боимся, что длина вектора как-либо изменится.
Конструкция value in object
означает то же самое, что и object.__contains(value)
.
def __contains__(self, value): return value in self.__values
Если вы хотите удалить какой-либо элемент коллекции, используя конструкцию del object[key]
, то фактически мы вызываем следующий метод: object.__delitem__(key)
.
def __delitem__(self, key):
del self.__values[key]
self = Vector(self.__values)
Что важно, мы recreate экземпляр класса Vector
, дабы обновить поле shape
.
Операции сравнения
Далее рассмотрим операции сравнения. Ниже приведена таблица соответствия операций и магических методов, отвечающих за них.
object < value
—object.__lt__(value)
object > value
—object.__gt__(value)
object <= value
—object.__le__(value)
object >= value
—object.__ge__(value)
object == value
—object.__eq__(value)
object != value
—object.__ne__(value)
В этой статье мы не будем реализовывать булеву логику NumPy, потому реализуем два метода __eq__
и __ne__
, а при вызове остальных будем возбуждать исключение.
def eq(self, value):
return all([v1 == v2 for v1, v2 in zip(self, value)])
def __ne__(self, value):
return not self.__eq__(value)
def __lt__(self, value):
raise TypeError('Vector class does not support comparison operations.')
def __gt__(self, value):
raise TypeError('Vector class does not support comparison operations.')
def __le__(self, value):
raise TypeError('Vector class does not support comparison operations.')
def __ge__(self, value):
raise TypeError('Vector class does not support comparison operations.')
Тестирование
Протестируем новые возможности, которые мы реализовали:
>>> v = Vector(1, 2, 3) >>> v[1], v[0:2], v[[1, 0, 2]] (2, Vector(<1, 2>), Vector(<2, 1, 3>))
>>> del v[0]
>>> v
Vector(<2, 3>)
>>> v = (3, 2, 1) >>> v == (3, 2, 1), v != (1, 2) (True, True)
В качестве бонуса: reversed(object)
выполняет то же самое, что и object.__reversed__()
. Реализуем и такой метод в нашем классе:
def __reversed__(self):
rev_values = reversed(self.__values)
return Vector(rev_values)
>>> v = Vector(1, 2, 3) >>> v = reversed(v) >>> v Vector(<3, 2, 1>)
Заключение
В этой статье мы дополнили наш класс вектора. Я специально не пытаюсь нагрузить читателей обзором всех методов. Сделано это с целью ознакомить вас с применением «магии» на практике, а не просто привести табличку соответствий. Желаю вам, чтобы магические методы были и в вашей жизни :)
Обновленный код класса вы сможете найти здесь.
На этом статья заканчивается. Если вы заинтересованы в математическом анализе, советую прочитать «Производная. Базовые определения и термины«.
А также подписывайтесь на группу ВКонтакте, Telegram и YouTube-канал. Там еще больше полезного и интересного для программистов.