Деньги удобно складывать плюсом: Money(220) + Money(140). Чтобы так можно было писать со своим классом,
операторы тоже задают магическими методами. Это называется перегрузка операторов (operator
overloading) — у оператора появляется смысл для нового типа.
__add__: плюс
a + b вызывает a.__add__(b). Метод возвращает новый объект — результат сложения. Если тип второго
слагаемого чужой, метод, как и __eq__, возвращает NotImplemented:
class Weight:
def __init__(self, grams):
self.grams = grams
def __repr__(self):
return f"Weight({self.grams})"
def __add__(self, other):
if not isinstance(other, Weight):
return NotImplemented
return Weight(self.grams + other.grams) # новый объект
a = Weight(250)
b = Weight(100)
print(a + b, a, b)
print(a + b + Weight(50))Weight(350) Weight(250) Weight(100) Weight(400)
class BadWeight:
def __init__(self, grams):
self.grams = grams
def __repr__(self):
return f"BadWeight({self.grams})"
def __add__(self, other):
self.grams += other.grams # ошибка: меняет левое слагаемое
return self
bag = BadWeight(250)
total = bag + BadWeight(100)
print(total, bag, total is bag)BadWeight(350) BadWeight(350) True
__radd__: когда ваш объект справа
sum(список) начинает с нуля: 0 + первый + второй…. Первое сложение — 0 + Weight(…): у числа 0 нет
сложения с весом, его __add__ возвращает NotImplemented. Тогда Python пробует отражённый метод
второго объекта: Weight(…).__radd__(0) (r — от reflected). Если и его нет — TypeError:
sum([Weight(250), Weight(100)])TypeError: unsupported operand type(s) for +: 'int' and 'Weight'
Обычно __radd__ для sum устроен так: ноль слева — вернуть себя, иначе — то же, что __add__.
Допишите класс Money: Money(220) + Money(140) — это Money(360), слагаемые не меняются. Сложение
с чужим типом — NotImplemented. Добавьте __radd__, чтобы sum([Money(220), Money(140)]) давал
Money(360).
__add__ устроен как у Weight; результат — новый Money с суммой рублей.__radd__ повторяет правило из абзаца выше: ноль слева — вернуть self, иначе — то же, что __add__.Заготовка упражнения обновилась с тех пор, как вы сохранили свой код.
Вернуть заготовку? Ваш код в этом упражнении будет удалён.
class Money:
def __init__(self, rubles):
self.rubles = rubles
def __repr__(self):
return f"Money({self.rubles})"
def __eq__(self, other):
if not isinstance(other, Money):
return NotImplemented
return self.rubles == other.rubles
def __add__(self, other):
...
def __radd__(self, other):
...Упражнение пока не решено, поэтому для ячеек ниже выполнено эталонное решение — ваш код не изменён.
class Money:
def __init__(self, rubles):
self.rubles = rubles
def __repr__(self):
return f"Money({self.rubles})"
def __eq__(self, other):
if not isinstance(other, Money):
return NotImplemented
return self.rubles == other.rubles
def __add__(self, other):
if not isinstance(other, Money):
return NotImplemented
return Money(self.rubles + other.rubles)
def __radd__(self, other):
if other == 0:
return self
return self.__add__(other)__mul__ и __rmul__: умножение на число
a * 3 вызывает a.__mul__(3), а 3 * a — сначала int.__mul__, который не умеет, а потом a.__rmul__(3).
Для коммутативного умножения на число __rmul__ обычно просто вызывает __mul__:
Допишите класс Grams (вес в граммах, атрибут value): Grams(18) * 3 и 3 * Grams(18) — это Grams(54).
Умножать можно только на целое число (int); на что-то другое — NotImplemented.
__add__, но множитель сравнивайте с int, а не с Grams.__rmul__ может просто вызвать self.__mul__(number).Заготовка упражнения обновилась с тех пор, как вы сохранили свой код.
Вернуть заготовку? Ваш код в этом упражнении будет удалён.
class Grams:
def __init__(self, value):
self.value = value
def __repr__(self):
return f"Grams({self.value})"
def __eq__(self, other):
if not isinstance(other, Grams):
return NotImplemented
return self.value == other.value
def __mul__(self, number):
...
def __rmul__(self, number):
...Упражнение пока не решено, поэтому для ячеек ниже выполнено эталонное решение — ваш код не изменён.
class Grams:
def __init__(self, value):
self.value = value
def __repr__(self):
return f"Grams({self.value})"
def __eq__(self, other):
if not isinstance(other, Grams):
return NotImplemented
return self.value == other.value
def __mul__(self, number):
if not isinstance(number, int):
return NotImplemented
return Grams(self.value * number)
def __rmul__(self, number):
return self.__mul__(number)У класса есть только __mul__, а __rmul__ нет. Что даст 3 * Grams(18)?
3 * Grams(18) сначала спрашивает int.__mul__, он отвечает NotImplemented. Потом Python ищет Grams.__rmul__ — а его нет. Переставлять множители сам Python не будет: для своих типов он не знает, коммутативно ли умножение.Операторы в деле
С операторами код кассы читается как формула:
Сохраните в revenue сумму всех чеков из checks — одним вызовом sum, а в beans — сколько зёрен уйдёт
на cups чашек, если на чашку нужно Grams(18).
__radd__; для зёрен — одного умножения.Заготовка упражнения обновилась с тех пор, как вы сохранили свой код.
Вернуть заготовку? Ваш код в этом упражнении будет удалён.
checks = [Money(640), Money(1250), Money(380), Money(910)]
cups = 120
revenue = ...
beans = ...Упражнение пока не решено, поэтому для ячеек ниже выполнено эталонное решение — ваш код не изменён.
checks = [Money(640), Money(1250), Money(380), Money(910)]
cups = 120
revenue = sum(checks)
beans = Grams(18) * cups