在啥情况下调用__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 = 24Bar,但是分配给obj2 会产生错误,因为Bar 没有__mul__ 并且Foo 没有__rmul__

我希望这已经足够清楚了。

【讨论】:

【参考方案3】:

__mul__() 是点积的情况,点积的结果应该是一个标量或只是一个数字,即__mul__() 会产生类似x1*x2+y1*y2 的点积乘法。在 __rmul__() 中,结果是带有 x = x1*x2y = y1*y2 的点。

【讨论】:

以上是关于在啥情况下调用__rmul__?的主要内容,如果未能解决你的问题,请参考以下文章

从双方实现__rmul__,python

Linux信号函数的SIG_ERR在啥情况下会出现?

流畅python学习笔记:第十三章:重载运算符__add__,__iadd__,__radd__,__mul__,__rmul__,__neg__,__eq__,__invert__,__pos__(

python 内置函数

在使用 boost 共享互斥锁时,我应该在啥情况下使用 owns_lock() 函数

MySQL like 在啥情况下会走索引