2011-03-03 171 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__

我希望這已經夠清楚了。

相關問題