Нашли ошибку?
Скачать чистый
урок 3 из 4≈ 20 минупражнения: 0 из 3

Операторы: __add__ и компания

Перегрузка операторов: __add__ и __mul__ задают + и * для своего класса, __radd__ и __rmul__ — операции, где ваш объект справа (и sum). Неизменяемые объекты: оператор возвращает новый объект.

Python запустится при первом выполнении

Деньги удобно складывать плюсом: 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 не умеет складывать весошибка: TypeError
sum([Weight(250), Weight(100)])
Ошибкасохранённый · нажмите «Выполнить», чтобы получить свой
TypeError: unsupported operand type(s) for +: 'int' and 'Weight'

Обычно __radd__ для sum устроен так: ноль слева — вернуть себя, иначе — то же, что __add__.

[ ]Упражнение 1Деньги складываются решено

Допишите класс Money: Money(220) + Money(140) — это Money(360), слагаемые не меняются. Сложение с чужим типом — NotImplemented. Добавьте __radd__, чтобы sum([Money(220), Money(140)]) давал Money(360).

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):
        ...

__mul__ и __rmul__: умножение на число

a * 3 вызывает a.__mul__(3), а 3 * a — сначала int.__mul__, который не умеет, а потом a.__rmul__(3). Для коммутативного умножения на число __rmul__ обычно просто вызывает __mul__:

[ ]Упражнение 2Граммы на порцию решено

Допишите класс Grams (вес в граммах, атрибут value): Grams(18) * 3 и 3 * Grams(18) — это Grams(54). Умножать можно только на целое число (int); на что-то другое — NotImplemented.

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):
        ...

Вопрос

У класса есть только __mul__, а __rmul__ нет. Что даст 3 * Grams(18)?

Операторы в деле

С операторами код кассы читается как формула:

[ ]Упражнение 3Расход за смену решено

Сохраните в revenue сумму всех чеков из checks — одним вызовом sum, а в beans — сколько зёрен уйдёт на cups чашек, если на чашку нужно Grams(18).

checks = [Money(640), Money(1250), Money(380), Money(910)]
cups = 120
revenue = ...
beans = ...

Нашли ошибку?