__rmul__ 会在什么情况下被调用?

76

假设我有一个列表l,在什么情况下会调用l.__rmul__(self, other)方法?

我基本上理解了文档,但我还想看一个例子,以消除任何疑虑。

3个回答

167

当Python试图对两个对象进行乘法运算时,它首先尝试调用左侧对象的__mul__()方法。如果左侧对象没有__mul__()方法(或该方法返回NotImplemented,表示它无法与所讨论的右操作数一起使用),那么Python想知道右侧对象是否可以执行乘法。如果右操作数与左操作数是相同类型的,则Python知道它不能,因为如果左侧对象不能执行乘法,同一类型的另一个对象肯定也不能执行乘法。

但是,如果这两个对象是不同类型的,那么Python认为值得一试。然而,它需要通过某种方式告诉右侧对象它是在操作中的正确对象,以防操作不可交换。 (乘法当然是可交换的,但并非所有运算符都是,并且无论如何,*并不总是用于乘法!)因此,Python调用__rmul__()而不是__mul__()

例如,考虑以下两个语句:

print "nom" * 3
print 3 * "nom"
在第一种情况下,Python调用字符串的__mul__()方法。字符串知道如何将自己乘以整数,所以一切正常。在第二种情况下,整数不知道如何将自己乘以字符串,因此它的__mul__()返回NotImplemented,然后调用字符串的__rmul__()。它知道该怎么做,你得到与第一种情况相同的结果。
现在我们可以看到__rmul__()允许所有字符串的特殊乘法行为被包含在str类中,使得其他类型(如整数)不需要知道任何关于字符串的东西就能够乘以它们。一百年后(假设Python仍在使用),你将能够定义一种新类型,它可以按任意顺序与整数相乘,即使int类已经超过一个世纪没有了解它的存在。
顺便说一句,在某些版本的Python中,字符串类的__mul__()存在错误。如果它不知道如何将自己乘以一个对象,它会引发TypeError而不是返回NotImplemented。这意味着即使用户定义的类型具有__rmul__()方法,你也不能将字符串乘以它,因为字符串从不让它有机会。用户定义的类型必须先出现(例如Foo() * 'bar'而不是'bar' * Foo()),这样它的__mul__()就会被调用。他们似乎已经在Python 2.7中修复了这个问题(我也在Python 3.2中测试过),但Python 2.6.6存在此错误。

2
kindall说:现在你已经接受了一个答案,这可能是徒劳的努力,但是: 我想向您保证,您的努力并没有白费——我在启用向量代数中的__rmul__使用(用于向量的标量乘法)时遇到了问题。您的解释足以让我相信,在(标量)*(向量)操作的情况下,__mul__方法应该以"raise NotImplementedError()"或"return Not Implemented"结尾,以启用调用__rmul__方法。谢谢您的帮助! - user377367
4
实际上,即使是乘法本身,一旦你开始思考更一般的数学结构,它也不总是可交换的。例如,考虑矩阵乘法。 - Shaun Harker
6
这个回答的第一句话并不严格正确。当右侧对象是左侧对象类型的子类实例时,右侧对象将有第一次处理该操作的机会。 - wim

12

二元运算符的本质是有两个操作数。每个操作数可以在运算符的左侧或右侧。当您为某种类型重载运算符时,可以指定要对哪一侧的运算符进行重载。当在不同类型的两个操作数上调用运算符时,这非常有用。以下是一个示例:

class Foo(object):
    def __init__(self, val):
        self.val = val

    def __str__(self):
        return "Foo [%s]" % self.val


class Bar(object):
    def __init__(self, val):
        self.val = val

    def __rmul__(self, other):
        return Bar(self.val * other.val)

    def __str__(self):
        return "Bar [%s]" % self.val


f = Foo(4)
b = Bar(6)

obj = f * b    # Bar [24]
obj2 = b * f   # ERROR

在这里,obj将是一个具有val = 24Bar,但对obj2的赋值会生成一个错误,因为Bar没有__mul__而且Foo没有__rmul__

希望这足够清晰了。


0

__mul__() 是点积的情况,点积的结果应该是一个标量或者仅仅是一个数字,即 __mul__() 的结果是类似于 x1*x2+y1*y2 的点积乘法。在 __rmul__() 中,结果是一个点,其中 x = x1*x2y = y1*y2


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