class операторов - Перегрузка оператора в Python:обработка различных типов и порядок параметров




ооп методы (3)

Вам также необходимо реализовать __rmul__ . Когда исходный вызов int.__mul__(7, v) завершается с ошибкой, Python будет затем попробовать type(v).__rmul__(v, 7) .

def __rmul__(self, lhs):
    return self * lhs  # Effectively, turn 7 * v into v * 7

Как указывает Rawing, вы можете просто написать __rmul__ = __mul__ для этого определения. __rmul__ существует, чтобы разрешить некоммутативное умножение, когда просто отложить до __mul__ с обратными операндами недостаточно.

Например, если вы пишете класс Matrix и хотите поддерживать умножение вложенным списком, например,

m = Matrix(...)  # Some 2 x 2 matrix
n = [[1, 2], [3,4]]
p = n * m

Здесь класс list не знал бы, как несколько экземпляров экземпляра Matrix , поэтому, когда list.__mul__(n, m) терпит неудачу, Python попытается затем использовать Matrix.__rmul__(m, n) . Однако n * m и m * n представляют собой два разных результата в целом, поэтому Matrix.__rmul__(m, n) != Matrix.__mul__(m, n) ; __rmul__ должен сделать небольшую дополнительную работу для получения правильного ответа.

На этот вопрос уже есть ответ:

У меня есть простой класс, который помогает с математическими операциями над векторами (т.е. списками чисел). Мой Vector можно умножить на другие экземпляры Vector или скаляр ( float или int ).

В других, более строго типизированных языках я бы создал метод для умножения двух vector s и отдельный метод для умножения vector by и int / float . Я все еще довольно новичок в Python и не знаю, как это реализовать. Единственный способ, которым я могу это сделать, - переопределить __mul__() и проверить входящий параметр:

class Vector(object):
  ...
 def __mul__(self, rhs):
  if isinstance(rhs, Vector):
     ...
  if isinstance(rhs, int) or isinstance(rhs, float):
    ...

Даже если я сделаю так, я буду вынужден умножить Vector на такой скаляр:

v = Vector([1,2,3])

result = v * 7

Что делать, если я хотел бы изменить порядок операндов в умножении?

result = 7 * v

Каков правильный способ сделать это в Python?


Существуют специальные методы для обратных операций :

  • __rmul__ для обратной __mul__
  • и __radd__ для __add__ ,
  • ...

Они NotImplemented когда оператор левой руки возвращает NotImplemented для нормальной работы (поэтому сначала операция 2 + vector_instance попытается: (2).__add__(vector_instance) но если это возвращает NotImplemented тогда vector_instance.__radd__(2) ).

Однако я бы не использовал проверки isinstance в арифметических специальных методах, что приведет к много повторения кода.

Фактически вы можете создать специальный случай в __init__ и реализовать преобразование из скаляров в Vector :

class Vector(object):
    def __init__(self, x, y=None, z=None):
        if y is None and z is None:
            if isinstance(x, Vector):
                self.x, self.y, self.z = x.x, x.y, x.z
            else:
                self.x, self.y, self.z = x, x, x
        elif y is None or z is None:
            raise ValueError('Either x, y and z must be given or only x')
        else:
            self.x, self.y, self.z = x, y, z

    def __mul__(self, other):
        other = Vector(other)
        return Vector(self.x*other.x, self.y*other.y, self.z*other.z)

    __rmul__ = __mul__   # commutative operation

    def __sub__(self, other):
        other = Vector(other)
        return Vector(self.x-other.x, self.y-other.y, self.z-other.z)

    def __rsub__(self, other):   # not commutative operation
        other = Vector(other)
        return other - self

    def __repr__(self):
        return 'Vector({self.x}, {self.y}, {self.z})'.format(self=self)

Это должно работать так, как ожидалось:

>>> 2 - Vector(1, 2, 3)
Vector(1, 0, -1)

>>> Vector(1, 2, 3) - 2
Vector(-1, 0, 1)

>>> Vector(1, 2, 3) * 2
Vector(2, 4, 6)

>>> 2 * Vector(1, 2, 3)
Vector(2, 4, 6)

Обратите внимание, что это был быстрый и грязный проект (который может иметь несколько ошибок). Я просто хотел представить «общую идею» о том, как ее можно было бы решить без специального обтекания типа в каждой арифметической операции.


Последнее предпочтительнее, поскольку оно правильно обрабатывает подклассы. Фактически, ваш пример можно записать еще проще, потому что второй параметр isinstance() может быть кортежем:

if isinstance(b, (str, unicode)):
    do_something_else()

или, используя basestring абстрактный класс:

if isinstance(b, basestring):
    do_something_else()




python class operator-overloading operators