Перейти к содержанию

Магические (dunder) методы

Работа с NotImplemented для бинарных операций

Рассмотрим следующий код:

class Vector2D:

    def __init__(self, x: int = 0, y: int = 0):
        self.x = x
        self.y = y

    def __mul__(self, scalar: int):
        return Vector2d(self.x * scalar, self.y * scalar)

Если в scalar попадёт float, то типы поменяются. Как правило, чтобы такого не было, можно добавить дополнительную проверку следующего вида:

if not isinstance(scalar, int):
  ... # что нужно написать здесь?

Первой мыслью наверное будет "сделать raise NotImplemented или AttributeError", но она в корне не верна.

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

При возврате NotImplemented интерпретатор попытается произвести "отраженную" операцию - для x.__mul__(y) попытается вызвать y.__mul__(x). Как только все возможные методы вернут NotImplemented он сам возбудит исключение на месте вызова метода, а не в коде класса.

Поэтому, этот код надо переписать вот так:

class Vector2D:

    def __init__(self, x: int = 0, y: int = 0):
        self.x = x
        self.y = y

    def __mul__(self, scalar: int):
        if isinstance(scalar, int):
            return Vector2d(self.x * scalar, self.y * scalar)
        else:
            return NotImplemented # обратите внимание, здесь return, а не raise

После чего, "стек вызовов" будет выглядеть как-то так:

v = Vector2D(1, 1)
x = v * 23.4
    # v.__mul__(23.4) -> return NotImplemented
    # float(23.4).__mul__(v) -> return NotImplemented
    # вариантов не осталось, интерпретатор делает raise TypeError
TypeError: unsupported operand type(s) for *: 'Vector2D' and 'float'

Подробнее можно почитать здесь

Когда использовать __str__ или __repr__?

Оба магических метода используются для получения строкового представления объекта. __repr__ должен использоваться для предоставлении информации об объекте разработчику, а __str__ должен быть читаемым и использоваться для представления информации пользователю.