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

Python中的运算符重载:处理不同类型和顺序的参数,python,class,operator-overloading,operators,Python,Class,Operator Overloading,Operators,我有一个简单的类,帮助对向量(即数字列表)进行数学运算。MyVector可以与Vector或标量(float或int的其他实例相乘 在另一种更强类型的语言中,我将创建一个方法,将两个向量相乘,并创建一个单独的方法,将向量与int/

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

在另一种更强类型的语言中,我将创建一个方法,将两个
向量
相乘,并创建一个单独的方法,将
向量
int
/相乘。我对Python还是相当陌生,不确定如何实现它。我能想到的唯一方法是重写
\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)