在啥情况下调用__rmul__?
Posted
技术标签:
【中文标题】在啥情况下调用__rmul__?【英文标题】:Under what circumstances are __rmul__ called?在什么情况下调用__rmul__? 【发布时间】:2011-07-08 01:39:00 【问题描述】:假设我有一个列表l
。在什么情况下调用l.__rmul__(self, other)
?
我基本了解文档,但我也想看一个例子来毫无疑问地阐明它的用法。
【问题讨论】:
【参考方案1】:当 Python 尝试将两个对象相乘时,它首先尝试调用左侧对象的 __mul__()
方法。如果左侧对象没有__mul__()
方法(或者该方法返回NotImplemented
,表明它不适用于所讨论的右侧操作数),那么Python 想知道右侧对象是否可以进行乘法运算。如果右边的操作数和左边的类型相同,Python 知道它不能,因为如果左边的对象做不到,另一个相同类型的对象肯定也做不到。
如果这两个对象是不同的类型,Python 认为值得一试。但是,它需要某种方式告诉正确的对象它是操作中的正确对象,以防操作不是可交换的。 (当然,乘法是,但不是所有的运算符都是,在任何情况下*
并不总是用于乘法!)所以它调用__rmul__()
而不是__mul__()
。
例如,考虑以下两个语句:
print "nom" * 3
print 3 * "nom"
在第一种情况下,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 有这个错误。
【讨论】:
kindall 说:既然您已经接受了答案,这可能是浪费精力,但是:我想向您保证,您的努力没有浪费 - 我在启用 rmul 在向量代数中使用(用于向量的标量乘法)。您的解释足以让我相信,在 (scalar) * (vector) 操作的情况下,mul 方法应该以“raise NotImplementedError()”或“return Not Implemented”结束,以启用对转到 rmul 方法。感谢您的帮助! 实际上,一旦您开始考虑更一般的数学结构,即使是乘法本身也不总是可交换的。例如,考虑矩阵乘法。 这个答案的第一句话并不严格正确。当右侧对象是左侧对象类型的子类的实例时,右侧对象将首先获得处理操作的机会。【参考方案2】:二元运算符本质上具有两个操作数。每个操作数可以在运算符的左侧或右侧。当您为某种类型重载运算符时,您可以指定在运算符的哪一侧完成重载。这在对不同类型的两个操作数调用运算符时很有用。这是一个例子:
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__
。
我希望这已经足够清楚了。
【讨论】:
【参考方案3】:__mul__()
是点积的情况,点积的结果应该是一个标量或只是一个数字,即__mul__()
会产生类似x1*x2+y1*y2
的点积乘法。在 __rmul__()
中,结果是带有 x = x1*x2
和 y = y1*y2
的点。
【讨论】:
以上是关于在啥情况下调用__rmul__?的主要内容,如果未能解决你的问题,请参考以下文章
流畅python学习笔记:第十三章:重载运算符__add__,__iadd__,__radd__,__mul__,__rmul__,__neg__,__eq__,__invert__,__pos__(