Python中的运算符重载:处理不同类型和顺序的参数
我有一个简单的类,帮助对向量(即数字列表)进行数学运算。MyPython中的运算符重载:处理不同类型和顺序的参数,python,class,operator-overloading,operators,Python,Class,Operator Overloading,Operators,我有一个简单的类,帮助对向量(即数字列表)进行数学运算。MyVector可以与Vector或标量(float或int的其他实例相乘 在另一种更强类型的语言中,我将创建一个方法,将两个向量相乘,并创建一个单独的方法,将向量与int/
Vector
可以与Vector
或标量(float
或int
的其他实例相乘
在另一种更强类型的语言中,我将创建一个方法,将两个向量
相乘,并创建一个单独的方法,将向量
与int
/\uuuu mul\uuu()
并测试传入参数:
class Vector(object):
...
def __mul__(self, rhs):
if isinstance(rhs, Vector):
...
if isinstance(rhs, int) or isinstance(rhs, float):
...
即使这样做,我也会被迫将向量
乘以如下标量:
v = Vector([1,2,3])
result = v * 7
如果我想颠倒乘法中操作数的顺序呢
result = 7 * v
在Python中,正确的方法是什么?您还需要实现
\uuurmul\uuu
。当对int.\uuuuMul\uuu7,v)
的初始调用失败时,Python下一步将尝试type(v)。\uuurmul\uu7)
正如Rawing所指出的,您可以简单地为这个定义编写\uuuuurmul\uuuuu=\uuuuuuuuu mul\uuuu
\uuuuRMul\uuuuuuuu
的存在是为了允许非交换乘法,而仅仅遵从操作数反转的\uuuuuuu mul\uuuuuu
是不够的
例如,如果您正在编写一个矩阵
类,并且希望支持嵌套列表的乘法,例如
m = Matrix(...) # Some 2 x 2 matrix
n = [[1, 2], [3,4]]
p = n * m
在这里,list
类不知道如何通过一个Matrix
实例将一个列表乘以,因此当list.\uuuuu mul(n,m)
失败时,Python下一步将尝试Matrix.\uu rmul\uuuu(m,n)
。然而,n*m
和m*n
通常是两个不同的结果,因此矩阵。uu\rmul_uu(m,n)!=矩阵.uuu mul(m,n)
<代码>\uuurmul\uuuuu必须做一些额外的工作才能生成正确的答案。有:
为\uuuurmul\uuuuuuu
\uuuuuu mul\uuuu
- 和
中的\uuuuu radd\uuuuuu
\uuuuu添加
NotImplemented
时调用这些函数(因此操作2+vector\u实例
将首先尝试:(2)。\uuuuu添加(vector\u实例)
但是如果返回NotImplemented
则调用vector\u实例
但是,我不会在算术特殊方法中使用isinstance
检查,这将导致大量代码重复
实际上,您可以在\uuuu init\uuuu
中创建一个特例,并在那里实现从标量到向量的转换:
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)
请注意,这是一个快速而肮脏的草稿(可能有几个bug)。我只是想提出一个“总体思路”,即如何在不使用特殊大小写的情况下解决每个算术运算中的类型。我个人只会使用NumPy
数组。它们已经支持元素操作(使用其他数组和标量)。它还包括内积、外积和累加函数,如sum
。这使得这项任务几乎微不足道。我知道我会得到这样的评论这是我用来熟悉Python的一次性类。我非常了解Numpy、Scipy、Pandas等。请看强类型和弱类型,它们不能说明语言是否存在运算符重载。斯卡拉和C++都有操作符重载。只有Java没有。谢谢你的“rmul=\uuuuuuuuuuuuuuuuuuuuuu”提示。我不知道这在Python中是可能的。你几乎肯定想做返回self.\uuu mul\uuu(lhs)
,而不是返回self*lhs
;后一个实现调用您已经运行的同一个“try left operator then right operator”机器,因此如果self.\uu mul\uuuu
返回NotImplemented
,并且lhs
有相同的\urmul\uuuuu
实现,则每个都会调用另一个(每次\uuuuuuuuuuuuuuuuuuuuu
返回未实现
)来回递归,直到堆栈溢出。显式调用\uuuuuuuuuuuuuuuuu
可避免潜在的无限递归。
>>> 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)