Python中的运算符重载:处理不同类型和参数的顺序

Rob*_*eph 16 python class operator-overloading operators

我有一个简单的类,可以帮助对向量进行数学运算(即数字列表).我Vector可以乘以其他实例Vector 标量(floatint).

在其他更强类型的语言中,我将创建一个方法来将两个vectors和一个单独的方法相乘以乘以vectorint/ float.我仍然是Python的新手,我不确定如何实现它.我能想到的唯一方法是覆盖__mul__()并测试传入的参数:

class Vector(object):
  ...
 def __mul__(self, rhs):
  if isinstance(rhs, Vector):
     ...
  if isinstance(rhs, int) or isinstance(rhs, float):
    ...
Run Code Online (Sandbox Code Playgroud)

即使我这样做,我也会被迫乘以这样Vector的标量:

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

result = v * 7
Run Code Online (Sandbox Code Playgroud)

如果我想在乘法中颠倒操作数的顺序怎么办?

result = 7 * v
Run Code Online (Sandbox Code Playgroud)

在Python中这样做的正确方法是什么?

che*_*ner 18

你还需要实现__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
Run Code Online (Sandbox Code Playgroud)

正如Rawing指出的那样,你可以简单地__rmul__ = __mul__为这个定义写.__rmul__存在允许非交换乘法,其中简单地推迟__mul__操作数被逆转是不够的.

例如,如果您正在编写一个Matrix类并希望通过嵌套列表支持乘法,例如,

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

这里,list类不知道如何通过Matrix实例复制列表,所以当list.__mul__(n, m)失败时,Python会接下来尝试Matrix.__rmul__(m, n).然而,n * mm * n在一般的两种不同的结果,所以Matrix.__rmul__(m, n) != Matrix.__mul__(m, n),__rmul__必须做一些额外的工作来产生正确的答案.

  • 你几乎肯定想做`return self .__ mul __(lhs)`,而不是'return self*lhs`; 后一个实现调用你已经运行的相同的"try left operator then right operator"机器,所以如果`self .__ mul__`返回`NotImplemented`,并且`lhs`具有相同的`__rmul__`实现,你最终会得到每个`__rmul__`调用另一个(在__mul__`之后每次返回`NotImplemented`)来回递送,直到你溢出堆栈.显式调用`__mul__`可以避免潜在的无限递归. (2认同)

MSe*_*ert 9

逆向操作特殊方法:

  • __rmul__ 反过来 __mul__
  • __radd__对于__add__,
  • ...

当左侧操作员返回NotImplemented正常操作时调用它们(因此操作2 + vector_instance将首先尝试:(2).__add__(vector_instance)但如果返回NotImplementedvector_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)
Run Code Online (Sandbox Code Playgroud)

这应该按预期工作:

>>> 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)
Run Code Online (Sandbox Code Playgroud)

请注意,这是一个快速而肮脏的草案(可能有几个错误).我只是想提出"一般的想法"如何在没有特殊套管的情况下解决每个算术运算中的类型.