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

时间:2017-06-13 12:07:16

标签: python class operator-overloading operators

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

在其他更强类型的语言中,我会创建一种方法来将两个vector和一个单独的方法相乘,以vector乘以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中这样做的正确方法是什么?

2 个答案:

答案 0 :(得分:17)

您还需要实施__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 * mm * n通常是两种不同的结果,因此Matrix.__rmul__(m, n) != Matrix.__mul__(m, n); __rmul__必须做一些额外的工作才能产生正确的答案。

答案 1 :(得分:8)

special methods for reversed operations

  • __radd____add__
  • 相反 {li>和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) 的转换:

declare @startdate date;

这应该按预期工作:

@startdate := DATEFROMPARTS(....

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