1.不能重載內置類型的運算符html
2.不能新建運算符,只能重載現有的python
3.某些運算符不能重載:is、and、or、notide
1 from array import array 2 import reprlib 3 import math 4 5 6 class Vector: 7 typecode = 'd' 8 shortcut_names = 'xyzt' 9 10 def __init__(self, components): 11 self._components = array(self.typecode, components) 12 13 def __iter__(self): 14 return iter(self._components) 15 16 def __repr__(self): 17 components = reprlib.repr(self._components) 18 components = components[components.find('['):-1] 19 return 'Vector({})'.format(components) 20 21 def __str__(self): 22 return str(tuple(self)) 23 24 def __bytes__(self): 25 return (bytes([ord(self.typecode)]) + bytes(self._components)) 26 27 def __eq__(self, other): 28 return tuple(self) == tuple(other) 29 30 def __abs__(self): 31 return math.sqrt(sum(x * x for x in self)) 32 33 def __bool__(self): 34 return bool(abs(self)) 35 36 @classmethod 37 def frombytes(cls, octets): 38 typecode = chr(octets[0]) 39 memv = memoryview(octets[1:]).cast(typecode) 40 return cls(memv)
- (__neg__) 一元取負運算符spa
+ (__pos__) 一元取正運算符3d
~ (__invert__) 對整數按位取反code
(__abs__) 取絕對值component
實現它們:orm
def __neg__(self): return Vector(-x for x in self) #建立一個新的Vector實例,把self的每一個份量都取反 def __pos__(self): return Vector(self) #建立一個新的Vector實例,傳入self各個份量
__invert__不實現是由於,計算~v時python會拋出TypeError,且輸出詳細的錯誤信息,這符合預期。htm
!!注意:x和+x可能不相等,例如
import decimal ctx = decimal.getcontext() #獲取當前全局算術運算的上下文引用 ctx.prec = 40 #把算術運算上下文精度設置爲40 one_third = decimal.Decimal('1') / decimal.Decimal('3') #計算1/3 print(one_third) print(one_third == +one_third) # ctx.prec = 28 #把精度下降爲28,默認精度 print(one_third == +one_third) print(+one_third) 0.3333333333333333333333333333333333333333 True False #one_third == +one_third返回False 0.3333333333333333333333333333 #小數點後位是28位而不是40位
兩個向量加在一塊兒生成一個新向量,若是兩個不一樣長度的Vector實例加一塊兒呢,能夠拋出錯誤,但最好是用0填充,那麼能夠:
def __add__(self, other): pairs = itertools.zip_longest(self, other, fillvalue=0) return Vector(a + b for a, b in pairs) #pairs是一個生成器,生成(a, b)形式元組,a來自self,b來自other,0填充
嘗試調用:
if __name__ == '__main__': V1 = Vector([3, 4, 5]) V2 = V1 + (2, 3, 4, 5) print(repr(V2)) V3 = (2, 3, 4, 5) + V1 print(repr(V3)) #結果 Vector([5.0, 7.0, 9.0, 5.0]) TypeError: can only concatenate tuple (not "Vector") to tuple
實現的加法能夠處理任何數值可迭代對象,可是對調操做數加法就會失敗。實際上a + b調用的是a.__add__(b),而b + a天然是調用b.__add__(a)。而(2,3,4,5)顯然沒有實現這樣的加法,如何解決?
爲了支持涉及不一樣類型的運算,python爲中綴運算符提供了特殊的分派機制。對於表達式a + b來講,解釋器會執行如下幾步操做:
(1)若是a有__add__方法,並且返回值不是NotImplemented,調用a.__add__(b),而後返回結果。
(2)若是a沒有__add__方法,或者__add__方法返回值是NotImplemented,檢查b有沒有__radd__方法,若是有並且沒有返回NotImplemented,調用b.__radd__(a),而後返回結果。
(3)若是b沒有__radd__方法,或者__radd__方法返回值是NotImplemented,拋出TypeError,並在錯誤消息中指明操做類型不支持。
__radd__是__add__的反向版本。那麼加法能夠這樣寫:
def __add__(self, other): pairs = itertools.zip_longest(self, other, fillvalue=0) return Vector(a + b for a, b in pairs) def __radd__(self, other): return self + other #__radd__直接委託__add__
新問題是,若是操做類型是單個數或者‘helloworld’這樣的字符串拋出的錯誤不合理:
V1 = Vector([3, 4, 5]) V2 = V1 + 1 #TypeError: zip_longest argument #2 must support iteration V1 = Vector([3, 4, 5]) V2 = V1 + 'abc' #TypeError: unsupported operand type(s) for +: 'float' and 'str'
因爲類型不兼容而致使運算符特殊方法沒法返回有效的結果,那麼應該返回NotImplemented,而不是TypeError。返回NotImplemented時,另外一個操做數所屬的類型還有機會執行運算,即python會嘗試調用反向方法。
def __add__(self, other): try: pairs = itertools.zip_longest(self, other, fillvalue=0) return Vector(a + b for a, b in pairs) except TypeError: return NotImplemented def __radd__(self, other): return self + other
發生TypeError時,解釋器嘗試調用反向運算符方法,若是操做數是不一樣的類型,對調以後可能反向運算符能夠正確計算。
向量 * x,若是x是數值,就是向量每一個份量乘以x。
def __mul__(self, other): return Vector(n * other for n in self) def __rmul__(self, other): return self * other
問題是:提供不兼容的操做數就會出問題,other須要的參數是數字,傳入bool類型,或者fractions.Fraction實例等就會出問題。
這裏採用類型檢測,可是不硬編碼具體類型,而是檢查numbers.Real(由於不多是複數)抽象基類,這個抽象基類涵蓋了全部可能能夠參與這個運算的類型。
def __mul__(self, other): if isinstance(self, numbers.Real): return Vector(n * other for n in self) else: return NotImplemented def __rmul__(self, other): return self * other
中綴運算符見表:
比較運算符區別於+ * / 等:
(1) 正向反向調用使用的是同一系列方法,規則如表13-2.例如__gt__方法調用反向__lt__方法,而且把參數對調。
(2)對於==和!=來講,若是反向調用失敗,python會比較對象ID,而不拋出TypeError。
僅僅比較長度以及各個份量顯然是不合理的,因該加上類型檢查:
def __eq__(self, other): if isinstance(other, Vector): return len(self) == len(other) and all(a == b for a, b in zip(self, other)) else: return NotImplemented
對於!=運算符,從object繼承的__ne__方法後備行爲知足了需求:定義了__eq__方法且不返回NotImplemented時,__ne__會對__eq__返回的結果取反。
(事實上可能x==y成立不表明x!=y不成立,有時須要定義__ne__方法)
object繼承的__ne__方法便是下面代碼的C語言版本:
def __ne__(self, other): eq_result = self == other if eq_result is NotImplemented: return NotImplemented else: return not eq_result
對於Vector類,它是不可變類型,是不能實現 += 這樣的就地方法的。
某可變的類型:
class B(A): def __add__(self, other): if isinstance(other, A): return B(self.某屬性+other.某屬性) else: return NotImplemented def __iadd__(self, other): if isinstance(other, A) other_iterable = other.某屬性 else: try: other_iterable = iter(other) except TpyeError: #拋出xxx錯誤 使用other_iterable更新self return self
以上來自《流暢的python》