python 运算符重载_Python3面向对象-运算符重载
1:運算符重載介紹
運算符重載,就是在某個類的方法中,攔截其內置的操作(比如:+,-,*,/,比較,屬性訪問,等等),使其實例的行為接近內置類型。
當類的實例出現在內置操作中時(比如:兩個實例相加 +),Python會自動調用你的方法(比如:你重載的__add__方法),并且你的方法的返回值會作為相應操作的結果。
Python3中的運算符重載:
運算符重載讓類攔截常規的Python操作。
類可以重載所有Python表達式運算符。
類也可以重載打印,函數調用,屬性訪問等內置運算。
重載是通過在一個類中提供特殊名稱的方法來實現的。
2:Python3中常見運算符重載方法
3:運算符重載方法示例
3.1:索引和分片:__getitem__和__setitem__
在實例進行 類似 X[2] 這種操作時會調用__getitem__方法;
在實例進行 類似 X[2] = value 這種操作時會調用__setitem__方法;索引:
分片:
# encoding=gbk class Test: def __getitem__(self, item): print('item:',item) if isinstance(item,int): return item**3 # 返回 x 的三次方 else: print('slicing',item.start,item.stop,item.step) return [x**3 for x in range(item.start,item.stop,item.step)] def __setitem__(self, key, value): print(key,value) # do something t = Test()# 索引:print(t[2]) # 會調用__getitem__函數, 返回2 的三次方print(t[3]) # 會調用__getitem__函數, 返回3 的三次方t[3] = 100 # 會調用__setitem__ print('*'*60)# 分片:print(t[2:10:2]) # 傳入的是分片對象t[2:5] = 1003.2:返回數值:__index__ (__index__不是索引)
在需要整型數字的上下文中,會調用__index__函數,__index__會為實例返回一個整數值。比如:調用函數hex(X),bin(X)時,會去調用X的__index__方法:
# encoding=gbk class Test: def __index__(self): return 100 X = Test()print(hex(X))print(bin(X))print(oct(X))3.3:可迭代對象:__iter__,__next__
如果要使自己定義的類的對象是可迭代的,那么就必須使這個類支持迭代協議,即重載__iter__,__next__方法。
迭代協議:(包括兩種對象)
可迭代對象(Iterable):里面包含了__iter__(); 可迭代對象X, 通過調用 I = iter(X) 可返回一個迭代器對象,再調用next(I) 就可以迭代X中的元素。
迭代器對象(Iterator):里面包含了__iter__() 和 __next__()
迭代過程:(for循環,等迭代中默認的操作)
首先調用 iter函數: I = iter(X); 調用的是X.__iter__()
然后對返回對象I調用next:next(I); 調用的是 I.__next__(),直到迭代完成。
3.3.1:單遍迭代
即只能迭代一次:
# encoding=gbk class Fibonacci: def __init__(self, n): self.a = 0 self.b = 1 self.max_cnt = n def __iter__(self): return self def __next__(self): self.a, self.b = self.b, self.a + self.b if self.a > self.max_cnt: raise StopIteration return self.a fib = Fibonacci(5)print(fib) print('1:'+'*' * 30)I = iter(fib) # 調用 fib.__iter__(),(返回的是self,及fib)print(next(I)) # 調用的是 fib.__next__(),fib對象中的a,b屬性值會改變。print(next(I)) # 調用的是 fib.__next__() print('1:'+'*' * 30) # for循環:首先調用的是I = iter(fib),此處返回的是self即fib,再調用next(I),即fib.__next__(),此時其值已經取完2個了,因此從第3個開始取。for x in fib: print(x)print('2:'+'*' * 30)# 此處與上面的循環一樣,但是fib.__next__()已經把數據取完了,故這里不會有輸出!for x in fib: print(x) print('3:'+'*' * 30) #i = iter(fib) # 返回self,即fibfor ii in i: # 與上面的 for x in fib 一樣,不會再輸出! print(ii)3.3.2:多遍迭代
即可以多次迭代使用:
# encoding=gbk class Fibonacci: def __init__(self, n): self.a = 0 self.b = 1 self.max_cnt = n def __iter__(self): return FibonacciIter(self.a,self.b,self.max_cnt) class FibonacciIter: def __init__(self,a,b,max_cnt): self.a = a self.b = b self.max_cnt = max_cnt def __next__(self): self.a, self.b = self.b, self.a + self.b if self.a > self.max_cnt: raise StopIteration return self.a fib = Fibonacci(5) I = iter(fib)print(next(I)) # 調用的是FibonacciIter對象中的__next__方法,fib對象中的a,b屬性沒有任何變化。print(next(I))print(next(I))print(next(I))print(next(I)) # 迭代完了# print(next(I)) # 上一步迭代完了,再次調用next(I)會拋出異常, print('1:'+'*' * 30) print(fib)print('1:'+'*' * 30)# for循環(默認調用):首先調用一次 I = iter(fib) 即fib.__iter__(),返回一個重新創建FibonacciIter對象, ;# 然后再調用 next(I),即I.__next__() (也就是FibonacciIter類中的__next__()函數),直到迭代完。for x in fib: print(x)print('2:'+'*' * 30)# 與上面for循環調用過程一樣。for x in fib: print(x) print('3:'+'*' * 30) # i = iter(fib)# for ii in i: # 這里會報錯,因為iter(fib)返回FibonacciIter的實例 i , # 而for循環首先會調用 iter(i) 即 調用FibonacciIter中的__iter__函數,而FibonacciIter類中沒有重載此函數;# print(ii)3.3.3:__iter__ 加 yield 實現多遍迭代
# encoding=gbk class Test: def __init__(self,start,stop): self.start = start self.stop = stop def __iter__(self): print(self.start,self.stop+1) for value in range(self.start,self.stop+1): yield value**2 t = Test(1,3)# 說明:for循環中,首先調用 I = iter(t),即調用的是t.__iter__(),在__iter__函數中有yield語句,# yield語句會自動創建一個包含 __next__ 方法的類,并返回它的實例,# 然后會調用 next(I),I 為yield自動創建類的實例for i in t: print(i)print('*'*40)for i in t: print(i)3.4:屬性訪問:__getattr__,__getattribute__和__setattr__
3.4.1 __getattr__,__getattribute__
__getattr__ 會攔截未定義的屬性,即在使用點號訪問屬性時(如:X.屬性) ,如果Python通過其繼承樹搜索過程中沒有找到這個屬性,那么就會自動調用__getattr__方法。
__getattribute__ 會攔截所有屬性。
# encoding=gbk class Test: aa = 0 def __init__(self): self.age = 100 def __getattr__(self, item): print('in __getattr__:',item) t = Test()print('1:' ,t.__dict__)# 屬性引用,屬性找不到時,就會調用__getattr__方法print(t.aa) # 在類中存在類屬性 aa,print('2:'+ '*' * 30) print(t.bb) # t.__dict__ 中不存在屬性 bb,其父類中也沒有屬性bb,故會調用__getattr__方法print('3:'+ '*' * 30)print(t.age) # 存在實例屬性age,不會調用__getattr__方法t.age = 200print('4:' ,t.__dict__) """1: {'age': 100}02:******************************in __getattr__: bbNone3:******************************1004: {'age': 200}"""3.4.2__setattr__
__setattr__:會攔截所有的屬性賦值
如果定義或者繼承了__setattr__方法,那么 self.attr = value,將會變成 self.__setattr__('attr',value)
這里要注意的是 如果在__setattr__方法中有使用 self.attr = value 的賦值形式,那么__setattr__將會進入死循環,因為self.attr = value 的賦值形式會調用self.__setattr__('attr',value),而__setattr__方法中又使用self.attr = value進行賦值,從而進入一個循環。
# encoding=gbk class Test: def __init__(self): # 構造函數中對 self.age 進行賦值,如果繼承了__setattr__方法, # 就會把self.age = 100 變成 self.__setattr__('age',100) self.age = 100 def __getattr__(self, item): print('in __getattr__:',item) def __setattr__(self, key, value): print('in __setattr__:',key,value) # self.aa = 100 # 這樣賦值會導致死循環,因為 self.aa = 100 會變成 self.__setattr__('aa',100),而后者又調用了前者 if key != 'age': # 攔截 age屬性 self.__dict__[key] = value t = Test()print('1:' + '*'*30)print(t.__dict__) # 此處輸出為{};雖然在構造函數中有self.age = 100賦值,但是在 __setattr__方法中把它過濾掉啦print('2:' + '*'*30)print(t.age) # 由于 age屬性被攔截掉了,故訪問t.age會調用 __getattr__方法print('3:' + '*'*30)t.age = 200 # 會把 age給攔截掉t.name = 'ixusy' # 不存在name屬性,因此會調用__setattr__,在__setattr__方法中把 name屬性 添加到屬性字典__dict__中, # 后面就可以通過使用t.name進行訪問。print('4:' + '*'*30)print(t.__dict__)print('5:' + '*'*30) """輸出結果:in __setattr__: age 1001:******************************{}2:******************************in __getattr__: ageNone3:******************************in __setattr__: age 200in __setattr__: name ixusy4:******************************{'name': 'ixusy'}5:******************************"""3.4.3 __getattr__ 和 __setattr__總結
__getattr__ :攔截不存在的屬性引用!
__setattr__:攔截所有的屬性賦值,當心死循環!
3.5:調用表達式:__call__
在實例上執行函數調用表達式,就會自動調用__call__函數
# encoding=gbk class Test: def __call__(self, *args, **kwargs): print('call:',args,kwargs) t = Test()t(1,2,3)t(1,2,3,b=22)# 傳遞參數,需要符合函數傳遞參數的規則# t(1,a=2,3,b=22) # 這樣傳遞會報錯3.6:字符串顯示:__str__ 和 __repr__
__str__ 和 __repr__ 都是用于顯示字符串,只不過是他們的使用場景不同而已。
__str__ :打印操作(print),內置函數str調用,會優先調用__str__ ,如果沒有重載__str__,就會去調用__repr__;
__repr__:用于所有其他場景:包括交互式命令行,repr函數,嵌套顯示,以及沒有可用__str__時,print和str的調用。
__repr__ 可用于任何地方,__str__用于print 和 str函數。
3.7:比較運算
# encoding=gbk class Person: def __init__(self,name,age,height): self.name = name self.age = age self.height = height # 比較規則可以自行定義, # 下面規則為: # 1:年齡小的 比較結果為小 # 2:年齡相等的,比較身高:身高小,結果為小 # 3:其他情況返回False def __lt__(self, other): if self.age < other.age: return True elif self.age == other.age: return self.height < other.height else: return False """ 還可以重載: __gt__ __le__ __ge__ __eq__ __ne__ 需要注意的是 p1 == p2,并不表示p1 != p2, 具體要看你怎么實現 __eq__,__ne__方法, 實際中盡可能使得__eq__,__ne__方法的實現符合正常的邏輯。 """ p1 = Person('ixusy88',18,188)p2 = Person('i',18,180)print(p1 < p2) # False p1 = Person('ixusy88',18,177)p2 = Person('i',18,180)print(p1 < p2) # True總結
以上是生活随笔為你收集整理的python 运算符重载_Python3面向对象-运算符重载的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 基于python的随机森林回归实现_随机
- 下一篇: java 栈和队列实现迷宫代码_Leet