【相關推薦:Python3視訊教學 】
大家在寫 Python 程式碼的時候有沒有這樣的疑問。
為什麼數學中的+
號,在字串運算中卻變成拼接功能,如'ab' + 'cd'
結果為abcd
;而*
號變成了重複功能,如'ab' * 2
結果為abab
。
為什麼某些物件print
能輸出資料,而print
自定義的類物件卻輸出一堆看不懂的程式碼<__main__.MyCls object at 0x105732250>
。
不是因為系統做了特殊客製化,而是 Python 中有一類特殊的方法,在某些特定的場合會自動呼叫。如,在字串類str
中定義了__add__
方法後,當程式碼遇到字串相加'ab' + 'cd'
時,就會自動呼叫__add__
方法完成字串拼接。
因為這類特殊方法的方法名都是以雙下劃線開始和結束,所以又被稱為雙下方法。
Python 中的雙下方法很多,今天我們對它做個詳解。
Python中的雙下方法
__init__
的方法是很多人接觸的第一個雙下方法
。
class A: def __init__(self, a): self.a = a
當呼叫A()
範例化物件的時候,__init__
方法會被自動呼叫,完成物件的初始化。
在類中定義運運算元相關的雙下方法
,可以直接在類物件上做加減乘除、比較等操作。
這裡,定義一個尺子類Rule
,它包含一個屬性r_len
代表尺子的長度。
class Rule: def __init__(self, r_len): self.r_len = r_len
如果想按照尺子的長度對不同的尺子做比較,需要在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
引數,完成比較。
下面是比較運運算元的雙下方法
比較運運算元雙下方法
可以支援類物件加減乘除。
def __add__(self, other): return Rule(self.r_len + other.r_len)
這裡定義了__add__
方法,對應的是+
運運算元,他會把兩個尺子的長度相加,並生成新的尺子。
rule1 = Rule(10) rule2 = Rule(5) rule3 = rule1 + rule2
下面是算術運運算元的雙下方法
它支援其他型別的變數與Rule
類相加。以__radd__
方法為例
def __radd__(self, other): return self.r_len + other
rule1 = Rule(10) rule2 = 10 + rule1
程式執行10 + rule1
時,會嘗試呼叫int
類的__add__
但int
類類沒有定義與Rule
類物件相加的方法,所以程式會呼叫+
號右邊物件rule1
的__radd__
方法,並把10
傳給other
引數。
所以這種運運算元又叫右加運運算元。它所支援的運運算元與上面的算術運運算元一樣,方法名前加r
即可。
增量賦值運運算元是+=
、-=
、*=
、/=
等。
def __iadd__(self, other): self.r_len += other return self
rule1 = Rule(10) rule1 += 5
除了__pmod__
方法,其他的跟算數運運算元一樣,方面名前都加i。
這部分支援按二進位制進行取反、移位和與或非等運算。由於Rule
類不涉及位運算,所以我們換一個例子。
定義二進位制字串的類BinStr
,包含bin_str
屬性,表示二進位制字串。
class BinStr: def __init__(self, bin_str): self.bin_str = bin_str
x = BinStr('1010') #建立二進位制字串物件 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
下面是位運運算元的雙下方法
這部分也支援反向位運運算元和增量賦值位運運算元,規則跟算數運運算元一樣,這裡就不再贅述。
這部分涉及兩個雙下方法__repr__
和__format__
,在某些特殊場景,如print
,會自動呼叫,將物件轉成字串。
還是以BinStr
為例,先寫__repr__
方法。
def __repr__(self): decimal = int('0b'+self.bin_str, 2) return f'二進位制字串:{self.bin_str},對應的十進位制數位:{decimal}'
x = BinStr('1011') print(x) # 輸出:二進位制字串:1011,對應的十進位制數位:11
當程式執行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
引數。
呼叫int(obj)
、float(obj)
等方法,可以將物件轉成相對應資料型別的資料。
def __int__(self): return int('0b'+self.bin_str, 2)
x = BinStr('1011') print(int(x))
當呼叫int(x)
時,會自動呼叫__int__
方法,將二進位制字串轉成十進位制數位。
數值轉換除了上面的兩個外,還有__abs__
、__bool__
、__complex__
、__hash__
、__index__
和__str__
。
__str__
和__repr__
一樣,在print
時都會被自動呼叫,但__str__
優先順序更高。
這部分可以像集合那樣,定義物件長度、獲取某個位置元素、切片等方法。
以__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]) # 101
len(x)
會自動呼叫__len__
返回物件的長度。
通過[]
方式獲取物件的元素時,會自動呼叫__getitem__
方法,並將切片物件傳給item
引數,即可以獲取單個元素,還可以獲取切片。
集合相關的雙下方法還包括__setitem__
、__delitem__
和__contains__
。
可以在物件上使用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
迴圈時,會先呼叫__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,對應的十進位制數位:13
做 web 開發的朋友,用類相關的雙下方法會更多一些。
範例的建立是__new__
和__init__
方法,範例的銷燬是__del__
方法。
__new__
的呼叫早於__init__
,它的作用是建立物件的範例(記憶體開闢一段空間),而後才將該範例傳給__init__
方法,完成範例的初始化。
由於__new__
是類靜態方法,因此它可以控制物件的建立,從而實現單例模式。
__del__
方法在範例銷燬時,被自動呼叫,可以用來做一些清理工作和資源釋放的工作。
類屬性的存取和設定。包括__getattr__
、__getattribute__
、__setattr__
和__delattr__
方法。
__getattr__
和__getattribute__
的區別是,當存取類屬性時,無論屬性存不存在都會呼叫__getattribute__
方法,只有當屬性不存在時才會呼叫__getattr__
方法。
控制屬性的存取,一般用於把屬性的取值控制在合理範圍內。包括__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]
之前,防止不合法的取值。
雖然上面介紹的不是所有的雙下方法,但也算是絕大多數了。
雖然雙下方法裡可以編寫任意程式碼,但大家儘量編寫與方法要求一樣的程式碼。如,在__add__
方法實現的不是物件相加而是相減,雖然也能執行,但這樣會造成很大困惑,不利於程式碼維護。
【相關推薦:Python3視訊教學 】
以上就是完全掌握Python中的雙下方法的詳細內容,更多請關注TW511.COM其它相關文章!