一文帶你了解Python中的雙下方法
前言
大家在寫 Python 代碼的時候有沒有這樣的疑問。
為什么數(shù)學中的+號,在字符串運算中卻變成拼接功能,如'ab' + 'cd'結果為abcd;而*號變成了重復功能,如'ab' * 2結果為abab。
為什么某些對象print能輸出數(shù)據(jù),而print自定義的類對象卻輸出一堆看不懂的代碼<__main__.MyCls object at 0x105732250>。
不是因為系統(tǒng)做了特殊定制,而是 Python 中有一類特殊的方法,在某些特定的場合會自動調用。如,在字符串類str中定義了__add__方法后,當代碼遇到字符串相加'ab' + 'cd'時,就會自動調用__add__方法完成字符串拼接。
因為這類特殊方法的方法名都是以雙下劃線開始和結束,所以又被稱為雙下方法。
Python 中的雙下方法很多,今天我們對它做個詳解。

Python中的雙下方法
1. init方法
__init__的方法是很多人接觸的第一個雙下方法。
class?A: ????def?__init__(self,?a): ????????self.a?=?a
當調用A()實例化對象的時候,__init__方法會被自動調用,完成對象的初始化。
2. 運算符的雙下方法
在類中定義運算符相關的雙下方法,可以直接在類對象上做加減乘除、比較等操作。
這里,定義一個尺子類Rule,它包含一個屬性r_len代表尺子的長度。
class?Rule: ????def?__init__(self,?r_len): ????????self.r_len?=?r_len
2.1 比較運算符
如果想按照尺子的長度對不同的尺子做比較,需要在Rule類中定義比較運算符。
class?Rule: ????def?__init__(self,?r_len): ????????self.r_len?=?r_len ????#?<?運算符 ????def?__lt__(self,?other): ????????return?self.r_len?<?other.r_len ????#?<=?運算符 ????def?__le__(self,?other): ????????return?self.r_len?<=?other.r_len ????#?>?運算符 ????def?__gt__(self,?other): ????????return?self.r_len?>?other.r_len ????#?>=?運算符 ????def?__ge__(self,?other): ????????return?self.r_len?>=?other.r_len
這里定義了<、<=、>和>=四個比較運算符,這樣就可以用下面的代碼比較Rule對象了。
rule1?=?Rule(10) rule2?=?Rule(5) print(rule1?>?rule2)??#?True print(rule1?>=?rule2)??#?True print(rule1?<?rule2)??#?False print(rule1?<=?rule2)??#?False
當用>比較rule1和rule2的時候,rule1對象會自動調用__gt__方法,并將rule2對象傳給other參數(shù),完成比較。
下面是比較運算符的雙下方法

比較運算符雙下方法
2.2 算術運算符
可以支持類對象加減乘除。
def?__add__(self,?other): ????return?Rule(self.r_len?+?other.r_len)
這里定義了__add__方法,對應的是+運算符,他會把兩個尺子的長度相加,并生成新的尺子。
rule1?=?Rule(10) rule2?=?Rule(5) rule3?=?rule1?+?rule2
下面是算術運算符的雙下方法

2.3 反向算術運算符
它支持其他類型的變量與Rule類相加。以__radd__方法為例
def?__radd__(self,?other): ????return?self.r_len?+?other
rule1?=?Rule(10) rule2?=?10?+?rule1
程序執(zhí)行10 + rule1時,會嘗試調用int類的__add__但int類類沒有定義與Rule類對象相加的方法,所以程序會調用+號右邊對象rule1的__radd__方法,并把10傳給other參數(shù)。
所以這種運算符又叫右加運算符。它所支持的運算符與上面的算術運算符一樣,方法名前加r即可。
2.4 增量賦值運算符
增量賦值運算符是+=、-=、*=、/=等。
def?__iadd__(self,?other): ????self.r_len?+=?other ????return?self
rule1?=?Rule(10) rule1?+=?5
除了__divmod__方法,其他的跟算數(shù)運算符一樣,方面名前都加i。
2.4 位運算符
這部分支持按二進制進行取反、移位和與或非等運算。由于Rule類不涉及位運算,所以我們換一個例子。
定義二進制字符串的類BinStr,包含bin_str屬性,表示二進制字符串。
class?BinStr: ????def?__init__(self,?bin_str): ????????self.bin_str?=?bin_str
x?=?BinStr('1010')??#創(chuàng)建二進制字符串對象
print(x.bin_str)?#?1010給BinStr定義一個取反運算符~
#?~?運算符 def?__invert__(self): ????inverted_bin_str?=?''.join(['1'?if?i?==?'0'?else?'0'?for?i?in?self.bin_str]) ????return?BinStr(inverted_bin_str)
__invert__方法中,遍歷bin_str字符串,將每位取反,并返回一個新的BinStr類對象。
x?=?BinStr('1011')
invert_x?=?~x
print(invert_x.bin_str)?#?0100下面是位運算符的雙下方法

這部分也支持反向位運算符和增量賦值位運算符,規(guī)則跟算數(shù)運算符一樣,這里就不再贅述。
3.字符串表示
這部分涉及兩個雙下方法__repr__和__format__,在某些特殊場景,如print,會自動調用,將對象轉成字符串。
還是以BinStr為例,先寫__repr__方法。
def?__repr__(self):
????decimal?=?int('0b'+self.bin_str,?2)
????return?f'二進制字符串:{self.bin_str},對應的十進制數(shù)字:{decimal}'x?=?BinStr('1011')
print(x)
#?輸出:二進制字符串:1011,對應的十進制數(shù)字:11當程序執(zhí)行print(x)時,會自動調用__repr__方法,獲取對象x對應的字符串。
再寫__format__方法,它也是將對象格式化為字符串。
def?__format__(self,?format_spec): ????return?format_spec?%?self.bin_str
print('{0:二進制字符串:%s}'.format(x))
#?輸出:二進制字符串:1011當.format方法的前面字符串里包含0:時,就會自動調用__format__方法,并將字符串傳給format_spec參數(shù)。
4.數(shù)值轉換
調用int(obj)、float(obj)等方法,可以將對象轉成相對應數(shù)據(jù)類型的數(shù)據(jù)。
def?__int__(self):
????return?int('0b'+self.bin_str,?2)x?=?BinStr('1011')
print(int(x))當調用int(x)時,會自動調用__int__方法,將二進制字符串轉成十進制數(shù)字。
數(shù)值轉換除了上面的兩個外,還有__abs__、__bool__、__complex__、__hash__、__index__和__str__。
__str__和__repr__一樣,在print時都會被自動調用,但__str__優(yōu)先級更高。
5.集合相關的雙下方法
這部分可以像集合那樣,定義對象長度、獲取某個位置元素、切片等方法。
以__len__和__getitem__為例
def?__len__(self): ????return?len(self.bin_str) def?__getitem__(self,?item): ????return?self.bin_str[item]
x?=?BinStr('1011')
print(len(x))??#?4
print(x[0])??#?1
print(x[0:3])??#?101len(x)會自動調用__len__返回對象的長度。
通過[]方式獲取對象的元素時,會自動調用__getitem__方法,并將切片對象傳給item參數(shù),即可以獲取單個元素,還可以獲取切片。
集合相關的雙下方法還包括__setitem__、__delitem__和__contains__。
6.迭代相關的雙下方法
可以在對象上使用for-in遍歷。
def?__iter__(self): ????self.cur_i?=?-1 ????return?self def?__next__(self): ????self.cur_i?+=?1 ????if?self.cur_i?>=?len(self.bin_str): ????????raise?StopIteration()??#?退出迭代 ????return?self.bin_str[self.cur_i]
x?=?BinStr('1011')
for?i?in?x:
????print(i)當在x上使用for-in循環(huán)時,會先調用__iter__方法將游標cur_i置為初始值-1,然后不斷調用__next__方法遍歷self.bin_str中的每一位。
這部分還有一個__reversed__方法用來反轉對象。
def?__reversed__(self):
????return?BinStr(''.join(list(reversed(self.bin_str))))x?=?BinStr('1011')
reversed_x?=?reversed(x)
print(reversed_x)
#?輸出:二進制字符串:1101,對應的十進制數(shù)字:137.類相關的雙下方法
做 web 開發(fā)的朋友,用類相關的雙下方法會更多一些。
7.1 實例的創(chuàng)建和銷毀
實例的創(chuàng)建是__new__和__init__方法,實例的銷毀是__del__方法。
__new__的調用早于__init__,它的作用是創(chuàng)建對象的實例(內存開辟一段空間),而后才將該實例傳給__init__方法,完成實例的初始化。
由于__new__是類靜態(tài)方法,因此它可以控制對象的創(chuàng)建,從而實現(xiàn)單例模式。
__del__方法在實例銷毀時,被自動調用,可以用來做一些清理工作和資源釋放的工作。
7.2 屬性管理
類屬性的訪問和設置。包括__getattr__、__getattribute__、__setattr__和__delattr__方法。
__getattr__和__getattribute__的區(qū)別是,當訪問類屬性時,無論屬性存不存在都會調用__getattribute__方法,只有當屬性不存在時才會調用__getattr__方法。
7.3 屬性描述符
控制屬性的訪問,一般用于把屬性的取值控制在合理范圍內。包括__get__、__set__和__delete__方法。
class?XValidation:
????def?__get__(self,?instance,?owner):
????????return?self.x
????def?__set__(self,?instance,?value):
????????if?0?<=?value?<=?100:
????????????self.x?=?value
????????else:
????????????raise?Exception('x不能小于0,不能大于100')
????def?__delete__(self,?instance):
????????print('刪除屬性')
class?MyCls:
????x?=?XValidation()
????def?__init__(self,?n):
????????self.x?=?n
obj?=?MyCls(10)
obj.x?=?101
print(obj.x)?#?拋異常:Exception: x不能小于0,不能大于100上述例子,通過類屬性描述符,可以將屬性x的取值控制在[0, 100]之前,防止不合法的取值。
8.總結
雖然上面介紹的不是所有的雙下方法,但也算是絕大多數(shù)了。
雖然雙下方法里可以編寫任意代碼,但大家盡量編寫與方法要求一樣的代碼。如,在__add__方法實現(xiàn)的不是對象相加而是相減,雖然也能運行,但這樣會造成很大困惑,不利于代碼維護。
到此這篇關于一文帶你了解Python中的雙下方法的文章就介紹到這了,更多相關Python雙下方法內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
python3中替換python2中cmp函數(shù)的實現(xiàn)
這篇文章主要介紹了python3替換python2中cmp函數(shù),文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2019-08-08

