流暢的python學習筆記:第十三章:重載運算符__add__,__iadd__,__radd__,__mul__,__rmul__,__neg__,__eq__,__invert__,__pos__


在前面第十章以及第一章的時候介紹了Vector對象的運算符重載。第十三章專門介紹運算符重載。這里我們看幾個之前沒講過的運算符__neg__,__pos__,__invert__
class Vector(object):
    def __init__(self,x):
        self.x=x
    def __neg__(self):
        return "Vector(%d)" % (-self.x)
    def __str__(self):
        return "Vector(%s)" % (str(self.data))
    def __iter__(self):
        return iter(self.data)
    def __pos__(self):
        return "Vector(%d)" % (self.x+1)
    def __invert__(self):
        return "Vector(%d)" % (~self.x)

if __name__=="__main__":
    v=Vector(1)
    print -v
    print +v
    print ~v
E:\python2.7.11\python.exe E:/py_prj/fluent_python/chapter13.py
Vector(-1)
Vector(2)
Vector(-2)
__neg__是在-v的時候調用
__pos__是在+v的時候調用
__invert__是在~v的時候調用
下面我們重新來看下+運算符。我們想實現兩個向量想加Vector([1,2,3])+Vector([1,2,3])得到[2,4,6].代碼如下
class Vector(object):
    def __init__(self,x):
        self.x=x
    def __add__(self, other):
        return [a+b for a,b in zip(self.x,other.x)]
if __name__=="__main__":
    v1=Vector([1,2,3])
    v2=Vector([1,2,3])
    print v1+v2
E:\python2.7.11\python.exe E:/py_prj/fluent_python/chapter13.py
[2, 4, 6]
在前面的例子中通過zip函數對兩個列表進行配對得到元組。[(1,1),(2,2),(3,3)]然后迭代進行加法運算最終得到結果[2,4,6]
但是如果兩個向量的格式變化一下。變成Vector([1,2,3])+Vector([1,2,3,4]).我們期望得到Vector([2,4,,6,4]), 向量Vector([1,2,3,4])由於比Vector([1,2,3])多一個數字,想加的時候最好是用零填充較短的那個向量。那么實際結果如何呢
if __name__=="__main__":
    v1=Vector([1,2,3])
    v2=Vector([1,2,3,4])
    print v1+v2
E:\python2.7.11\python.exe E:/py_prj/fluent_python/chapter13.py
[2, 4, 6]
我們看到結果和上次一樣還是[2,4,6]和我們預想的[2,4,6,4]相差太大。原因在於zip得到的結果是[(1, 1), (2, 2), (3, 3)],並沒有包含多出來的4。這種情況下我們就需要用到itertools.izip_lonest,這個方法會自動填充缺失的向量元素
class Vector(object):
    def __init__(self,x):
        self.x=x
    def __add__(self, other):
        pairs=itertools.izip_longest(self.x,other.x,fillvalue=0)
        return [a+b for a,b in pairs]
if __name__=="__main__":
    v1=Vector([1,2,3])
    v2=Vector([1,2,3,4])
    print v1+v2
E:\python2.7.11\python.exe E:/py_prj/fluent_python/chapter13.py
[2, 4, 6, 4]
izip_longest中,fillvalue代表填充的值。本次例子中設置的是0.因此最后一位是0+4=4
 
接下來我們的需求繼續變下。我們想實現Vector([1,2,3])+[1,2,3]也就是向量類和列表相加
class Vector(object):
    def __init__(self,x):
        self.x=x
    def __add__(self, other):
        if type(other) == list:
            pairs=itertools.izip_longest(self.x,other,fillvalue=0)
        else:
            pairs=itertools.izip_longest(self.x,other.x,fillvalue=0)
        return [a+b for a,b in pairs]
if __name__=="__main__":
    v1=Vector([1,2,3])
    v2=Vector([1,2,3,4])
    print v1+[1,2,3,4]
E:\python2.7.11\python.exe E:/py_prj/fluent_python/chapter13.py
[2, 4, 6, 4]
首先在__add__中判斷other的屬性,然后針對性的得到pairs
 
那如果是[1,2,3,4]+v1的結果會是什么呢?運行報錯。
E:\python2.7.11\python.exe E:/py_prj/fluent_python/chapter13.py
Traceback (most recent call last):
  File "E:/py_prj/fluent_python/chapter13.py", line 27, in <module>
    print [1,2,3,4]+v1
TypeError: can only concatenate list (not "Vector") to list
這是因為[1,2,3,4]並沒有add方法,只有Vector才有,這種場景需要用到__radd__方法
具體的運算流程圖如下:
1 如果a有__add__方法,則進行a.__add__(b)運算,否則返回Notimplemented然后檢查b有沒有__radd__方法。如果有,則調用b.__radd__(a),否則返回Notimplemented

我們在Vector中增加__radd__方法:
def __radd__(self, other):
    return self+other
__radd__直接委托__add__進行運算。代碼也很簡單。這下運算結果正確了。
E:\python2.7.11\python.exe E:/py_prj/fluent_python/chapter13.py
[2, 4, 6, 4]
同樣的方法也適用於乘法運算符__mul__和__rmul__。
 
下面來看下比較運算符以及反向比較在__eq__的作用。
首先定義__eq__的代碼,首先比較長度,然后比較對應的元素,如果都相等則返回True,否則返回False
def __eq__(self, other):
    if type(other) == list or type(other)==tuple:
        pairs=itertools.izip_longest(self.x,other,fillvalue=0)
        length=len(other)
    else:
        pairs=itertools.izip_longest(self.x,other.x,fillvalue=0)
        length=len(other.x)
    return (len(self.x)==length) and all(a==b for a,b in pairs)
if __name__=="__main__":
    v1=Vector([1.0,2.0,3.0])
    v2=Vector(range(1,4))
    print 'v1 compare v2 %s' % (v1==v2)
    v3=(1,2,3)
    print 'v1 compare v3 %s' % (v1==v3)
E:\python2.7.11\python.exe E:/py_prj/fluent_python/chapter13.py
v1 compare v2 True
v1 compare v3 True 
v1和v2相等這個沒啥疑問,但是當用元組(1,2,3)和向量v1相比的時候,得到的結果也是True,我們如果用[1,2]==(1,2)得到的結果為False,因為列表和元組不是一個類型。那么為什么向量和元組相比卻是相等的呢? 這就是問題所在了。在比較的時候並沒有比較類型。代碼更新如下:
def __eq__(self, other):
    if type(other) == list or type(other)==tuple:
        pairs=itertools.izip_longest(self.x,other,fillvalue=0)
        length=len(other)
    else:
        pairs=itertools.izip_longest(self.x,other.x,fillvalue=0)
        length=len(other.x)
    if isinstance(other,Vector): #在這里判斷是否屬於向量類
        return (len(self.x)==length) and all(a==b for a,b in pairs)
    else:
        return NotImplemented
再繼續看一個例子,增加一個Vector2d向量:
class Vector2d(object):
    def __init__(self,x):
        self.x=x
    def __eq__(self, other):
        return tuple(self.x) ==  tuple(other.x)
if __name__=="__main__":
    v1=Vector([1.0,2.0,3.0])
 
v4=Vector2d([1,2,3])
print v1==v4
E:\python2.7.11\python.exe E:/py_prj/fluent_python/chapter13.py
True
得到的結果為True. Vector2d不屬於Vector的實例呢。為什么會相等呢。這里用到了反向比較的方法
 
 
 
         (1)
  
  
  
          首先Vector.__eq__(v1,v4),v4不是Vector的實例。返回Notimplemented。然后嘗試調用Vector2d.__eq__(v4.v1)
 
 
 
         (2)
  
  
  
          Vector2d.__eq__(v4.v1)把兩個操作數都變成元組。然后比較。結果為True。
其實在用Vector和元組進行比較的時候,也是一樣的首先Vector.__eq__(v1,v4),返回Notimplemented。接着開始調用tuple.__eq__(v3,v1),但是tuple不知道vector是什么,因此返回Notimplemented。對於==來說,如果反向調用也是Notimplemented,則會進行最后的嘗試,比較對象的ID,如果仍然不相等則返回False。
 
接下來我們看下!=運算符
print v1!=v4
得到的結果是True,但是我們並沒有實現__neq__的方法,也能得到對應的結果,其實__neq__不用實現,只要定義了__eq__的方法,__neq__則會自動對__eq__的結果取反。
 
最后來看下增量賦值運算符:
if __name__=="__main__":
    v1=Vector([1.0,2.0,3.0])
    print id(v1)⑴
    v2=Vector(range(1,4))
    v1+=v2⑵
    print v1
    print id(v1)⑶
E:\python2.7.11\python.exe E:/py_prj/fluent_python/chapter13.py
25012400
[2.0, 4.0, 6.0]
25005672
(1)中,得到的id(v1)=25012400。 
(2)中,經過v1+=v2后。在(3)中打印id(v1)等於25005672.和之前第一步的不一樣了,也就是創建了新的實例。
如果我們想實現就地運算,不生成新的實例,就在v1的基礎上想加呢。這就需要實現__iadd__方法。當執行+=的時候會自動調用__iadd__方法,而如果沒有實現__iadd__方法,則會自動調用__add__方法
def __iadd__(self, other):
    if type(other) == list:
        pairs=itertools.izip_longest(self.x,other,fillvalue=0)
    else:
        pairs=itertools.izip_longest(self.x,other.x,fillvalue=0)
    i=0
    for p in pairs:
        self.x[i]=p[0]+p[1]
        i+=1
    return self
__iadd__方法中,返回的是self. 再來看下執行結果:
E:\python2.7.11\python.exe E:/py_prj/fluent_python/chapter13.py
25274544
Vector([2.0, 4.0, 6.0])
25274544
得到的實例是同一樣

 

 

 
       


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM