Магические методы python сложение

Магические методы __add__, __sub__, __mul__, __truediv__

Обратите внимание как записан параметр seconds. После него стоит двоеточие и указан ожидаемый тип данных. Эта нотация языка Python подсказывает программисту, какой тип данных следует передавать в качестве seconds. Конечно, мы можем передавать и другие типы данных, строгого ограничения здесь нет, это лишь пометка для программиста и не более того. Поэтому далее внутри инициализатора мы делаем проверку, что параметр seconds должен являться целым числом. Если это не так, генерируем исключение TypeError. Далее в этом же классе пропишем метод get_time для получения текущего времени в виде форматной строки:

def get_time(self): s = self.seconds % 60 # секунды m = (self.seconds // 60) % 60 # минуты h = (self.seconds // 3600) % 24 # часы return f"::" @classmethod def __get_formatted(cls, x): return str(x).rjust(2, "0")

Здесь дополнительно определен метод класса для форматирования времени (добавляется незначащий первый ноль, если число меньше 10). Далее, мы можем воспользоваться этим классом, например, так:

c1 = Clock(1000) print(c1.get_time())

Если же нам понадобиться изменить время в объекте c1, то сейчас это можно сделать через локальное свойство seconds:

c1.seconds = c1.seconds + 100

Конечно, при запуске программы возникнет ошибка, так как оператор сложения не работает с экземплярами класса Clock. Однако, это можно поправить, если добавить в наш класс магический метод __add__. Я запишу его в следующем виде:

def __add__(self, other): if not isinstance(other, int): raise ArithmeticError("Правый операнд должен быть типом int") return Clock(self.seconds + other)

И теперь, при запуске программы, все работает так, как и задумывалось. Давайте разберем этот момент подробнее. Вначале у нас есть объект класса Clock со значением секунд 1000. Затем, арифметическая операция c1 = c1 + 100 фактически означает выполнение команды:

Читайте также:  Java file дата создания

В результате, активируется метод __add__ и параметр other принимает целочисленное значение 100. Проверка проходит и формируется новый объект класса Clock со значением секунд 1000+100 = 1100. Этот объект возвращается методом __add__ и переменная c1 начинает ссылаться на этот новый экземпляр класса. На прежний уже не будет никаких внешних ссылок, поэтому он будет автоматически удален сборщиком мусора. Вас может удивить сложность процессов, когда нам всего лишь нужно прибавить 100 секунд к уже имеющемуся значению. Но эта сложность оправданна. Чтобы это понять, мы расширим функционал оператора сложения и допустим, что можно складывать два разных объекта класса Clock, следующим образом:

c1 = Clock(1000) c2 = Clock(2000) c3 = c1 + c2 print(c3.get_time())

Конечно, если сейчас запустить программу, то увидим ошибку ArithmeticError, так как параметр other не соответствует целому числу. Поправим это и немного изменим реализацию метода __add__:

def __add__(self, other): if not isinstance(other, (int, Clock)): raise ArithmeticError("Правый операнд должен быть типом int или объектом Clock") sc = other if isinstance(other, int) else other.seconds return Clock(self.seconds + sc)

Теперь, в программе можно складывать и отдельные целые числа и объекты классов Clock. Видите, как это удобно! Кроме того, мы можем прописывать и более сложные конструкции при сложении, например, такие:

c1 = Clock(1000) c2 = Clock(2000) c3 = Clock(3000) c4 = c1 + c2 + c3 print(c4.get_time())

И она сработала благодаря тому, что метод __add__ возвращает каждый раз новый экземпляр класса Clock. Детальнее все выглядит так. Сначала идет сложение объектов c1 + c2, в результате формируется новый объект класса Clock со значением секунд 1000 + 2000 = 3000. Пусть на этот класс ведет внутренняя переменная t1. Затем, для этого нового объекта вызывается снова метод __add__ и идет сложение с объектом t1 + c3. Получаем еще один объект с числом секунд 6000. На этот объект, как раз и будет ссылаться переменная c4, а объект с t1 будет автоматически уничтожен сборщиком мусора. Если бы мы не создавали экземпляры классов Clock в методе __add__ и не возвращали их, то конструкцию с двумя сложениями было бы невозможно реализовать. Еще одним важным нюансом работы оператора сложения для объектов классов, является порядок их записи. Мы всегда прописывали его в виде:

и это очевидно, так как здесь, фактически идет вызов метода: 100.__add__(c1) но он не существует для объекта int и экземпляров класса Clock. Как выйти из этой ситуации? Очень просто. Язык Python предоставляет нам специальный набор магических методов с добавлением буквы r: __radd__() Он автоматически вызывается, если не может быть вызван метод __add__(). Давайте добавим его определение в наш класс Clock:

def __radd__(self, other): return self + other

Смотрите, мы здесь записали команду сложения текущего объекта класса Clock с параметром other, который может быть или числом или тоже объектом класса Clock. В свою очередь будет вызван метод __add__, но с правильным порядком типов данных, поэтому сложение пройдет без ошибок. Наконец, у всех магических методов, связанных с арифметическими операторами, есть еще одна модификация с первой буквой i: __iadd__() Она вызывается для команды:

Если запустить сейчас программу, то никаких ошибок не будет и отработает метод __add__(). Но в методе __add__ создается новый объект класса Clock, тогда как при операции += этого делать не обязательно. Поэтому я добавлю еще один магический метод __iadd__ в наш класс Clock:

def __iadd__(self, other): print("__iadd__") if not isinstance(other, (int, Clock)): raise ArithmeticError("Правый операнд должен быть типом int или объектом Clock") sc = other if isinstance(other, int) else other.seconds self.seconds += sc return self

Смотрите, мы здесь не создаем нового объекта, а меняем число секунд в текущем. Это логичнее, так как вызывать цепочкой операцию += не предполагается и, кроме того, она изменяет (по смыслу) состояние текущего объекта. Поэтому я посчитал правильным добавить этот магический метод. Вот мы с вами подробно рассмотрели работу одного арифметического магического метода __add__() с его вариациями __radd__() и __iadd__(). По аналогии используются и все остальные подобные магические методы:

Оператор Метод оператора Оператор Метод оператора
x + y __add__(self, other) x += y __iadd__(self, other)
x — y __sub__(self, other) x -= y __isub__(self, other)
x * y __mul__(self, other) x *= y __imul__(self, other)
x / y __truediv__(self, other) x /= y __itruediv__(self, other)
x // y __floordiv__(self, other) x //= y __ifloordiv__(self, other)
x % y __mod__(self, other) x %= y __imod__(self, other)

Предлагаю вам в качестве самостоятельного задания добавить некоторые из них в класс Clock по аналогии с методами __add__(). При этом обращайте внимание на недопустимость дублирования кода. Оно произойдет, если вы «в лоб» будете прописывать другие арифметические магические методы. Подумайте, как все сделать правильно. На этом мы завершим это занятие по магическим методам. На следующем продолжим эту тему и поговорим о реализации операторов сравнения. Курс по Python ООП: https://stepik.org/a/116336

Источник

Перегрузка операторов

Python 3 логотип

Перегрузка операторов — один из способов реализации полиморфизма, когда мы можем задать свою реализацию какого-либо метода в своём классе.

Например, у нас есть два класса:

 В данном примере класс B наследует класс A, но переопределяет метод go, поэтому он имеет мало общего с аналогичным методом класса A.

Однако в python имеются методы, которые, как правило, не вызываются напрямую, а вызываются встроенными функциями или операторами.

Например, метод __init__ перегружает конструктор класса. Конструктор — создание экземпляра класса.

 

__new__(cls[, . ]) — управляет созданием экземпляра. В качестве обязательного аргумента принимает класс (не путать с экземпляром). Должен возвращать экземпляр класса для его последующей его передачи методу __init__.

__init__(self[, . ]) - как уже было сказано выше, конструктор.

__del__(self) - вызывается при удалении объекта сборщиком мусора.

__repr__(self) - вызывается встроенной функцией repr; возвращает "сырые" данные, использующиеся для внутреннего представления в python.

__str__(self) - вызывается функциями str, print и format. Возвращает строковое представление объекта.

__bytes__(self) - вызывается функцией bytes при преобразовании к байтам.

__format__(self, format_spec) - используется функцией format (а также методом format у строк).

__le__(self, other) - x ≤ y вызывает x.__le__(y).

__eq__(self, other) - x == y вызывает x.__eq__(y).

__ne__(self, other) - x != y вызывает x.__ne__(y)

__gt__(self, other) - x > y вызывает x.__gt__(y).

__ge__(self, other) - x ≥ y вызывает x.__ge__(y).

__hash__(self) - получение хэш-суммы объекта, например, для добавления в словарь.

__bool__(self) - вызывается при проверке истинности. Если этот метод не определён, вызывается метод __len__ (объекты, имеющие ненулевую длину, считаются истинными).

__getattr__(self, name) - вызывается, когда атрибут экземпляра класса не найден в обычных местах (например, у экземпляра нет метода с таким названием).

__setattr__(self, name, value) - назначение атрибута.

__delattr__(self, name) - удаление атрибута (del obj.name).

__call__(self[, args. ]) - вызов экземпляра класса как функции.

__len__(self) - длина объекта.

__getitem__(self, key) - доступ по индексу (или ключу).

__setitem__(self, key, value) - назначение элемента по индексу.

__delitem__(self, key) - удаление элемента по индексу.

__iter__(self) - возвращает итератор для контейнера.

__reversed__(self) - итератор из элементов, следующих в обратном порядке.

__contains__(self, item) - проверка на принадлежность элемента контейнеру (item in self).

Перегрузка арифметических операторов

__add__(self, other) - сложение. x + y вызывает x.__add__(y).

__sub__(self, other) - вычитание (x - y).

__mul__(self, other) - умножение (x * y).

__truediv__(self, other) - деление (x / y).

__floordiv__(self, other) - целочисленное деление (x // y).

__mod__(self, other) - остаток от деления (x % y).

__divmod__(self, other) - частное и остаток (divmod(x, y)).

__pow__(self, other[, modulo]) - возведение в степень (x ** y, pow(x, y[, modulo])).

__lshift__(self, other) - битовый сдвиг влево (x

__rshift__(self, other) - битовый сдвиг вправо (x >> y).

__and__(self, other) - битовое И (x & y).

__xor__(self, other) - битовое ИСКЛЮЧАЮЩЕЕ ИЛИ (x ^ y).

__or__(self, other) - битовое ИЛИ (x | y).

__rtruediv__(self, other),

__rfloordiv__(self, other),

__rdivmod__(self, other),

__rlshift__(self, other),

__rrshift__(self, other),

__ror__(self, other) - делают то же самое, что и арифметические операторы, перечисленные выше, но для аргументов, находящихся справа, и только в случае, если для левого операнда не определён соответствующий метод.

Например, операция x + y будет сначала пытаться вызвать x.__add__(y), и только в том случае, если это не получилось, будет пытаться вызвать y.__radd__(x). Аналогично для остальных методов.

__itruediv__(self, other) - /=.

__ifloordiv__(self, other) - //=.

__ipow__(self, other[, modulo]) - **=.

__ilshift__(self, other) -

__irshift__(self, other) - >>=.

__neg__(self) - унарный -.

__pos__(self) - унарный +.

__abs__(self) - модуль (abs()).

__invert__(self) - инверсия (~).

__complex__(self) - приведение к complex.

__int__(self) - приведение к int.

__float__(self) - приведение к float.

__round__(self[, n]) - округление.

__enter__(self), __exit__(self, exc_type, exc_value, traceback) - реализация менеджеров контекста.

Рассмотрим некоторые из этих методов на примере двухмерного вектора, для которого переопределим некоторые методы:

  В заключение хочу сказать, что перегрузка специальных методов - вещь хорошая, но не стоит ей слишком злоупотреблять. Перегружайте их только тогда, когда вы уверены в том, что это поможет пониманию программного кода.

Для вставки кода на Python в комментарий заключайте его в теги

  • Книги о Python
  • GUI (графический интерфейс пользователя)
  • Курсы Python
  • Модули
  • Новости мира Python
  • NumPy
  • Обработка данных
  • Основы программирования
  • Примеры программ
  • Типы данных в Python
  • Видео
  • Python для Web
  • Работа для Python-программистов

Источник

Оцените статью