2011-03-03 94 views

回答

83

当Python试图乘以两个对象时,它首先尝试调用左对象的方法__mul__()。如果左对象没有__mul__()方法(或者方法返回NotImpemented,表明它不适用于所讨论的右操作数),那么Python想知道正确的对象是否可以执行乘法。如果右操作数与左操作数是相同的类型,Python知道它不能,因为如果左对象不能这样做,另一个同类型的对象肯定不能。

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

作为一个例子,考虑以下两个语句:

print "nom" * 3 
print 3 * "nom" 

在第一种情况下,Python会自动调用该字符串的__mul__()方法。字符串知道如何乘以一个整数本身,所以一切都很好。在第二种情况下,整数不知道如何乘以一个字符串,所以它的__mul()__返回NotImplemented,并调用字符串的__rmul()__。它知道该怎么做,并且获得与第一种情况相同的结果。

现在我们可以看到,__rmul()__允许字符串的特殊繁殖行为的所有被包含在str类等其他类型(如整数)不需要知道字符串什么能够繁殖被他们。从现在开始的一百年(假设Python仍在使用中),您将能够定义一种新的类型,它可以按任意顺序乘以一个整数,即使类已经超过一个世纪都不知道它。

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


+2

以前接受的答案没什么不对,但如果有人在将来出现在这里,他们可能想在顶部碰到这个答案,所以我会接受它。 – porgarmingduod 2011-03-03 15:51:59

+1

++,优秀的答案 - 我学到了一些东西:) – 2011-03-03 17:18:17

+0

kindall说:这可能是你已经接受了答案,但现在浪费了精力: 我想向你保证你的努力没有被浪费 - 我遇到了问题使向量代数中的__rmul__有用(用于向量的标量乘法)。你的解释足以说服我,在(标量)*(矢量)操作的情况下,__mul__方法应该以“raise NotImplementedError()”或“return Not Implemented”结束,以使调用进入__rmul__方法。感谢您的帮助! – user377367 2011-05-07 01:13:26

7

二元运算符本质上有两个操作数。每个操作数可能在操作符的左侧或右侧。当您为某种类型重载操作符时,可以指定操作符的哪一侧执行重载操作。当在不同类型的两个操作数上调用操作符时,这非常有用。这里有一个例子:

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将是一个Barval = 24,但分配给obj2产生一个错误,因为Bar没有__mul__Foo没有__rmul__

我希望这已经够清楚了。

相关问题