假设我有一个列表l
,在什么情况下会调用l.__rmul__(self, other)
方法?
我基本上理解了文档,但我还想看一个例子,以消除任何疑虑。
当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
类已经超过一个世纪没有了解它的存在。__mul__()
存在错误。如果它不知道如何将自己乘以一个对象,它会引发TypeError
而不是返回NotImplemented
。这意味着即使用户定义的类型具有__rmul__()
方法,你也不能将字符串乘以它,因为字符串从不让它有机会。用户定义的类型必须先出现(例如Foo() * 'bar'
而不是'bar' * Foo()
),这样它的__mul__()
就会被调用。他们似乎已经在Python 2.7中修复了这个问题(我也在Python 3.2中测试过),但Python 2.6.6存在此错误。二元运算符的本质是有两个操作数。每个操作数可以在运算符的左侧或右侧。当您为某种类型重载运算符时,可以指定要对哪一侧的运算符进行重载。当在不同类型的两个操作数上调用运算符时,这非常有用。以下是一个示例:
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 = 24
的Bar
,但对obj2
的赋值会生成一个错误,因为Bar
没有__mul__
而且Foo
没有__rmul__
。
希望这足够清晰了。
__mul__()
是点积的情况,点积的结果应该是一个标量或者仅仅是一个数字,即 __mul__()
的结果是类似于 x1*x2+y1*y2
的点积乘法。在 __rmul__()
中,结果是一个点,其中 x = x1*x2
,y = y1*y2
。