回答
當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。
二元運算符本質上有兩個操作數。每個操作數可能在操作符的左側或右側。當您爲某種類型重載操作符時,可以指定操作符的哪一側執行重載操作。當在不同類型的兩個操作數上調用操作符時,這非常有用。這裏有一個例子:
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
將是一個Bar
與val = 24
,但分配給obj2
產生一個錯誤,因爲Bar
沒有__mul__
和Foo
沒有__rmul__
。
我希望這已經夠清楚了。
- 1. 在什麼情況下viewWillAppear被調用?
- 2. 在什麼情況下調用Application_EndRequest,但是Application_BeginRequest未被調用?
- 3. 爲什麼在以下情況下不會調用「onPause」?
- 4. 有什麼用在這種情況下
- 5. 在什麼情況下使用Django formset?
- 6. 在什麼情況下,該應用程序:didFinishLaunchingWithOptions:被調用?
- 7. 在UIView實現中,在什麼情況下drawRect被調用?
- 8. 如何在使用情況下在什麼情況下使用tsql?
- 9. 在什麼情況下調用Parse HTML事件?
- 10. 在什麼情況下C++析構函數不會被調用?
- 11. 在什麼情況下調用ELResolver的setValue方法?
- 12. 在什麼情況下,我們需要調用GC.Collect兩次
- 13. 在什麼情況下可以調用afx_msg void OnDestroy()又名CWnd:OnDestroy()?
- 14. 爲什麼setTimeOut在這種情況下不被調用?
- 15. 在什麼情況下不會調用Sybase觸發器?
- 16. 在什麼情況下會調用iOS系統推送通知?
- 17. 在什麼情況下UIElement.UpdateLayout()可以調用Environment.FailFast?
- 18. 什麼情況下**會有用?
- 19. fgets()在什麼情況下卡住了?
- 20. 在什麼情況下document.open()返回null?
- 21. 在什麼情況下CopyOnWriteArrayList適合?
- 22. 爲什麼在這三種情況下
- 23. 在什麼情況下AppDomain.DoCallback()會失敗?
- 24. 在什麼情況下glGenBuffers/glGenBuffersARB失敗?
- 25. 「:」在這種情況下做什麼?
- 26. 在什麼情況下會http.ListenAndServe返回
- 27. sched_yield在這種情況下做什麼?
- 28. 什麼是在這種情況下
- 29. *在這種情況下做什麼?:
- 30. ConcurrentBag.TryTake()在什麼情況下會失敗?
以前接受的答案沒什麼不對,但如果有人在將來出現在這裏,他們可能想在頂部碰到這個答案,所以我會接受它。 – porgarmingduod 2011-03-03 15:51:59
++,優秀的答案 - 我學到了一些東西:) – 2011-03-03 17:18:17
kindall說:這可能是你已經接受了答案,但現在浪費了精力: 我想向你保證你的努力沒有被浪費 - 我遇到了問題使向量代數中的__rmul__有用(用於向量的標量乘法)。你的解釋足以說服我,在(標量)*(矢量)操作的情況下,__mul__方法應該以「raise NotImplementedError()」或「return Not Implemented」結束,以使調用進入__rmul__方法。感謝您的幫助! – user377367 2011-05-07 01:13:26