如何重載向量加法運算符+

導語

小編一直都覺本身公衆號的排版很雞肋,從這篇文章開始將使用了新的排版風格,還特地地作了一個卡通二維碼(見文末),但願你們會喜歡(不要臉地僞裝有不少粉絲)。其實關於排版,小編要真心感謝一下景禹大佬的指導。好了,今天想跟你們談談如何重載運算符+,認真看完這篇文章,你將收穫:python

  • 瞭解中綴運算符特殊方法的分派機制
  • 瞭解向量類如何實現 add 方法
  • 瞭解向量類如何實現 radd 方法

a+b背後如何調用特殊方法

你們都知道若a和b都是同類型序列,a+b能夠實現序列接拼,若a和b都是int或者float等數值類型,a+b會實現數學上的加法,見示例1。spa

#示例1
a = (1,2)
b = (4,5)
print(a+b) #(1,2,4,5)
a = [1,2]
b = [4,5]
print(a+b) #[1,2,4,5]
a = b = 1
print(a+b) #2
a = b = 1.0
print(a+b) #2.0

可是,若是a和b是不一樣類型的序列,他們可否接拼成功呢?code

要回答這個問題,咱們先了解一下Python爲中綴運算符特殊方法提供的特殊分派機制,其流程見下圖。component

對於表達式a+b,爲了支持涉及不一樣類型的運算,Python解釋器會執行如下幾步操做。blog

  • 若是a有__ add 方法,返回值不是NotImplemented,調用a. add __(b),而後返回結果
  • 若是a沒有__ add 方法,或者調用 add 方法返回NotImplemented,檢查b有沒有 radd 方法,若是有,調用b. radd __(a)方法後沒有返回NotImplemented,返回結果。
  • 若是b沒有__ radd 方法,或者調用 radd __方法返回NotImplemented,拋出TypeError,並在錯誤消息中指明操做類型不支持。

radd 是 __ add __的「反向」版本,Alex、Anna和Leo幾位技術大佬喜歡稱之爲「右向」(right)特殊方法,由於他們都在右操做數上調用。ip

若是a和b是不一樣類型的序列,執行示例2的程序,會發生什麼呢?rem

#示例2
a = (1,2)
b = [3,4]
print(a+b)

首先解釋器會調用a.__ add (b)方法,由於a和b是不一樣類型的序列,因此返回NotImplemented。而後解釋器檢查b是否有b. radd __ (a) 方法,而後調用該方法,可是仍是返回NotImplemented,最終拋出TypeError結果。數學

TypeError: can only concatenate tuple (not "list") to tuple

重載運算符+

關於重載運算符,有幾點須要注意一下的:it

  • 不能重載內置類型的運算符,即list等這些內置類型的運算符不能重載。
  • 不能新建運算符,只能重載現有的。
  • 有些運算符不能重載,is、and、or和not(位運算&、|和~能夠)。

如今咱們嘗試定義一個Vector類,見示例3。class

#示例3
class Vector(object):
    def __init__(self,components):
        self.components = list(components)

若是咱們不對Vector重載運算符+,兩個Vector類相加拋出錯誤,見示例4

#示例4
v1 = Vector([1,2,3])
v2 = Vector([1,2,3])
print(v1+v2)
#TypeError: unsupported operand type(s) for +: 'Vector' and 'Vector'

好了,咱們如今立刻對Vector重載運算符+吧,見示例5,可是咱們不是實現接拼,而是實現數學上的加法,由於對於一個向量,接拼功能意義不大。

#示例5
class Vector(object):
    def __init__(self,components):
        self.components = list(components)
    
    def __iter__(self):
        return iter(self.components)
    
    def __str__(self):
        return str(self.components)

    def __add__(self,others):
       print("__add__")
       try:
            pairs = itertools.zip_longest(self,others,fillvalue=0.0) 
               return Vector(a+b for a,b in pairs)
       except TypeError:
            return NotImplemented

v1 = Vector([1,2,3])
v2 = Vector([1,2,3])
v3 = [1,2,3]
print(v1+v2)
print(v1+v3)

輸出結果:

__add__
[2,4,6]
__add__
[2,4,6]

從示例5中能夠看到,解析器調用了__ add __方法實現了兩個Vector或者Vector和具備數值元素的可迭代類型的相加。示例5可以實現V1+V3,那是否能實現V3+v1呢?見示例6。

#示例6
print(V3+V1)
#TypeError: can only concatenate list (not "Vector") to list

示例6拋出錯誤了,當解釋器執行v3.__ add (V1)時候,由於調用的是列表的 add 方法,不能與自定義的類型相加,因此返回結果NotImplemented,並嘗試執行V1. radd (V3)方法,可是在自定義的Vector類並無實現該方法,因此最後仍是拋出了TypeError。好了,如今嘗試實現Vector類的 radd __ 方法。在示例5的基礎上添加示例7的代碼。

#示例7
def __radd__(self,other):
    print("__radd__")
    return self+other

再次運行示例6,獲得以下輸出結果。

__radd__
__add__
[2, 4, 6]

當解釋器執行V3.__ add (V1)返回NotImplemented以後,就會調用自定義類中的 radd 方法, radd 方法把計算結果委託給 add __ ,事實上,任何可交換的運算符均可以這麼作。

好啦,以上就是就是小編今天分享的內容,但願對你有用。

公衆號:CVpython

專一於分享Python和計算機視覺,快點掃碼關注我吧!

相關文章
相關標籤/搜索