在自定义类中更改__add__、__mul__等方法的操作顺序

9

我有一个向量类:

class Vector:
    def __init__(self, x, y):
        self.x, self.y = x, y
    def __str__(self):
        return '(%s,%s)' % (self.x, self.y)
    def __add__(self, n):
        if isinstance(n, (int, long, float)):
            return Vector(self.x+n, self.y+n)
        elif isinstance(n, Vector):
            return Vector(self.x+n.x, self.y+n.y)

这个很好用,比如我可以写:

a = Vector(1,2)
print(a + 1) # prints (2,3)

然而,如果操作顺序颠倒,那么它就会失败:
a = Vector(1,2)
print(1 + a) # raises TypeError: unsupported operand type(s)
             #                   for +: 'int' and 'instance'

我理解这个错误:将一个int对象添加到一个Vector对象中是未定义的,因为我没有在int类中定义它。有没有一种方法可以在不定义int(或int的父类)类中的情况下解决这个问题?


你可能会发现这个有帮助:特殊方法名。还可以参考Rafe Kettler的《Python魔术方法指南》(A Guide to Python's Magic Methods)以及Python的魔术方法 - PM 2Ring
2个回答

10

你还需要定义__radd__

有些运算不一定像 a + b == b + a 这样计算,因此 Python 定义了addradd方法。

更好地解释:它支持“int”不将 class Vector 实例作为操作的一部分来定义+操作。因此,向量+1并不等于1+向量。

当Python尝试查看1.__add__方法可以做什么时,会引发异常。 然后Python继续寻找Vector.__radd__操作来尝试完成它。

在 OP 的情况下,评估结果为true并且足以使用__radd__ = __add__

class Vector(object):

    def __init__(self, x, y):
        self.x, self.y = x, y

    def __str__(self):
        return '(%s,%s)' % (self.x, self.y)

    def __add__(self, n):
        if isinstance(n, (int, long, float)):
            return Vector(self.x+n, self.y+n)
        elif isinstance(n, Vector):
            return Vector(self.x+n.x, self.y+n.y)

    __radd__ = __add__


a = Vector(1, 2)
print(1 + a)

输出:

(2,3)

对所有类似数字的操作都适用。


这里可能需要更多的解释? - Rob Murray
对于__mul____div__等是否也类似呢?那么__rmul____rdiv__等呢? - nluigi
如果操作中的第一个对象(一切皆为对象)不支持与第二个对象进行操作,则返回“是”。 - mementum
好的答案,我接受这个答案主要是因为提示__radd__=__add__,我喜欢一行解决方案 :) - nluigi
在你的情况下,这就是解决方案 ;) - mementum
我也喜欢这个提示,它可以避免实现 __radd__ 时不必要的额外工作。 - plamut

4
当你写 x + y 时,Python 会调用 x.__add__(y) 方法。如果 x 没有实现 __add__(或该方法返回 NotImplemented),Python 将尝试作为备选方案调用 y.__radd__(x)
因此,你只需要在 Vector 类中定义 __radd__() 方法,1 + y 就能按照你的预期工作了。
注意:你还需要为其他操作做类似的处理,例如实现 __mul__()__rmul__() 对等方法等。
你可能还想看看这个问题,它更详细地解释了同样的原理。
更新: 根据你的使用情况,你可能还想实现 __iadd__() 方法(以及其兄弟们)来覆盖 += 运算符。
例如,如果你写 y += 1(这里的 y 是 Vector 的一个实例),你可能希望修改 y 实例本身,而不是像你当前的 __add__() 方法那样返回一个新的 Vector 实例。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接