在什么情况下会调用__rmul__?
假设我有一个列表 l
。在什么情况下会调用 l.__rmul__(self, other)
这个方法呢?
我基本上理解了文档的内容,但我也想看看一个例子,以便更清楚地理解它的用法。
3 个回答
__mul__()
是用来计算点积的,点积的结果应该是一个标量,也就是一个数字。简单来说,__mul__()
进行的计算就像是 x1*x2+y1*y2
这样的乘法运算。而在 __rmul__()
中,结果是一个点,坐标是 x = x1*x2
和 y = y1*y2
。
二元运算符本身有两个操作数。每个操作数可以在运算符的左边或右边。当你为某种类型重载一个运算符时,你可以指定这个运算符的重载是针对左边的操作数还是右边的操作数。这在对两个不同类型的操作数使用运算符时特别有用。下面是一个例子:
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
是一个 Bar
类型,里面的 val
值是 24。但是给 obj2
赋值时会出错,因为 Bar
类型没有 __mul__
方法,而 Foo
类型也没有 __rmul__
方法。
希望这样解释够清楚。
当Python尝试把两个对象相乘时,它首先会调用左边对象的__mul__()
方法。如果左边的对象没有这个方法(或者这个方法返回NotImplemented
,表示它无法和右边的对象相乘),那么Python就会去看看右边的对象能不能进行相乘。如果右边的对象和左边的是同一种类型,Python就知道它也不能,因为如果左边的对象都不能,那同类型的另一个对象肯定也不行。
不过,如果这两个对象是不同类型的,Python觉得可以试试看。但是,它需要一种方式告诉右边的对象,它在这个操作中是“正确的对象”,以防这个操作不是可交换的(虽然乘法是可交换的,但并不是所有操作都是,而且*
也不总是用来表示乘法!)。所以它会调用__rmul__()
,而不是__mul__()
。
举个例子,看看下面的两个语句:
print "nom" * 3
print 3 * "nom"
在第一个情况下,Python调用了字符串的__mul__()
方法。字符串知道怎么把自己和一个整数相乘,所以一切正常。在第二个情况下,整数不知道怎么和字符串相乘,所以它的__mul__()
返回NotImplemented
,然后调用字符串的__rmul__()
。字符串知道该怎么做,结果和第一个情况是一样的。
现在我们可以看到,__rmul__()
让字符串的所有特殊乘法行为都包含在str
类中,这样其他类型(比如整数)就不需要了解字符串的任何信息,就能和它们相乘。假设一百年后(假设Python还在使用),你可以定义一个新的类型,它可以和整数进行任意顺序的相乘,即使int
类对这个新类型一无所知。
顺便提一下,字符串类的__mul__()
在某些版本的Python中有个bug。如果它不知道怎么和一个对象相乘,它会抛出TypeError
,而不是返回NotImplemented
。这意味着即使用户定义的类型有__rmul__()
方法,你也无法把字符串和这个用户定义的类型相乘,因为字符串根本不给它机会。用户定义的类型必须先出现(例如Foo() * 'bar'
而不是'bar' * Foo()
),这样它的__mul__()
才会被调用。他们似乎在Python 2.7中修复了这个问题(我在Python 3.2中也测试过),但Python 2.6.6仍然有这个bug。