在什么情况下__rmul__被称为?

por*_*uod 46 python operators

说我有一个清单l.在什么情况下被l.__rmul__(self, other)称为?

我基本上理解了文档,但我也希望看到一个例子,毫无疑问地澄清它的用法.

kin*_*all 114

当Python试图将两个对象相乘时,它首先尝试调用左对象的__mul__()方法.如果左对象没有__mul__()方法(或者方法返回NotImplemented,表明它不能与正确的操作数一起使用),那么Python想知道正确的对象是否可以进行乘法.如果右操作数与左边的操作数相同,那么Python知道它不能,因为如果左边的对象不能这样做,那么同一类型的另一个对象当然也不能.

但是,如果这两个对象是不同的类型,Python认为它值得一试.但是,如果操作不是可交换的,它需要某种方式告诉正确的对象它是操作中的正确对象.(乘法当然是,但并非所有运算符都是,并且在任何情况下*都不总是用于乘法!)所以它调用__rmul__()而不是__mul__().

例如,请考虑以下两个陈述:

print "nom" * 3
print 3 * "nom"
Run Code Online (Sandbox Code Playgroud)

在第一种情况下,Python调用字符串的__mul__()方法.字符串知道如何将自身乘以整数,所以一切都很好.在第二种情况下,整数不知道如何将自身乘以一个字符串,因此它的__mul()__返回NotImplemented和字符串__rmul()__被调用.它知道该怎么做,你得到的结果与第一种情况相同.

现在我们可以看到,__rmul()__允许所有字符串的特殊乘法行为包含在str类中,这样其他类型(如整数)就不需要知道任何有关字符串的信息就可以乘以它们.从现在起一百年后(假设Python仍在使用中),您将能够定义一个新类型,该类型可以按任意顺序乘以整数,即使int该类在一个多世纪以来一无所知.

顺便说一下,字符串类__mul__()在某些版本的Python中有一个错误.如果它不知道如何将自己乘以一个对象,它会引发一个TypeError而不是返回NotImplemented.这意味着即使用户定义的类型具有__rmul__()方法,也不能将字符串乘以用户定义的类型,因为字符串永远不会让它有机会.用户定义的类型必须首先(例如,Foo() * 'bar'而不是'bar' * Foo()),因此__mul__()调用它.他们似乎已经在Python 2.7中解决了这个问题(我也在Python 3.2中测试过它),但Python 2.6.6有这个bug.


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

Eli*_*sky 9

二元运算符本质上有两个操作数.每个操作数可以位于运算符的左侧或右侧.当您为某种类型的操作符重载时,您可以指定操作符的哪一侧完成重载.在两个不同类型的操作数上调用运算符时,这很有用.这是一个例子:

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
Run Code Online (Sandbox Code Playgroud)

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

我希望这很清楚.