完全掌握Python中的雙下方法

2022-07-21 18:00:25
本篇文章給大家帶來了關於Python的相關知識,Python中有一些特殊方法的方法名都是以雙下劃線開始和結束,所以又被稱為雙下方法,下面一起來看一下,希望對大家有幫助。

【相關推薦:Python3視訊教學

前言

大家在寫 Python 程式碼的時候有沒有這樣的疑問。

為什麼數學中的+號,在字串運算中卻變成拼接功能,如'ab' + 'cd'結果為abcd;而*號變成了重複功能,如'ab' * 2結果為abab

為什麼某些物件print能輸出資料,而print自定義的類物件卻輸出一堆看不懂的程式碼<__main__.MyCls object at 0x105732250>

不是因為系統做了特殊客製化,而是 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

當用>比較rule1rule2的時候,rule1物件會自動呼叫__gt__方法,並將rule2物件傳給other引數,完成比較。

下面是比較運運算元的雙下方法

比較運運算元雙下方法

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

程式執行10 + rule1時,會嘗試呼叫int類的__add__int類類沒有定義與Rule類物件相加的方法,所以程式會呼叫+號右邊物件rule1__radd__方法,並把10傳給other引數。

所以這種運運算元又叫右加運運算元。它所支援的運運算元與上面的算術運運算元一樣,方法名前加r即可。

2.4 增量賦值運運算元

增量賦值運運算元是+=-=*=/=等。

def __iadd__(self, other):
    self.r_len += other
    return self
rule1 = Rule(10)
rule1 += 5

除了__pmod__方法,其他的跟算數運運算元一樣,方面名前都加i。

2.4 位運運算元

這部分支援按二進位制進行取反、移位和與或非等運算。由於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

下面是位運運算元的雙下方法

這部分也支援反向位運運算元和增量賦值位運運算元,規則跟算數運運算元一樣,這裡就不再贅述。

3.字串表示

這部分涉及兩個雙下方法__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引數。

4.數值轉換

呼叫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__優先順序更高。

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])  # 101

len(x)會自動呼叫__len__返回物件的長度。

通過[]方式獲取物件的元素時,會自動呼叫__getitem__方法,並將切片物件傳給item引數,即可以獲取單個元素,還可以獲取切片。

集合相關的雙下方法還包括__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迴圈時,會先呼叫__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

7.類相關的雙下方法

做 web 開發的朋友,用類相關的雙下方法會更多一些。

7.1 範例的建立和銷燬

範例的建立是__new____init__方法,範例的銷燬是__del__方法。

__new__的呼叫早於__init__,它的作用是建立物件的範例(記憶體開闢一段空間),而後才將該範例傳給__init__方法,完成範例的初始化。

由於__new__是類靜態方法,因此它可以控制物件的建立,從而實現單例模式

__del__方法在範例銷燬時,被自動呼叫,可以用來做一些清理工作和資源釋放的工作。

7.2 屬性管理

類屬性的存取和設定。包括__getattr____getattribute____setattr____delattr__方法。

__getattr____getattribute__的區別是,當存取類屬性時,無論屬性存不存在都會呼叫__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.總結

雖然上面介紹的不是所有的雙下方法,但也算是絕大多數了。

雖然雙下方法裡可以編寫任意程式碼,但大家儘量編寫與方法要求一樣的程式碼。如,在__add__方法實現的不是物件相加而是相減,雖然也能執行,但這樣會造成很大困惑,不利於程式碼維護。

【相關推薦:Python3視訊教學

以上就是完全掌握Python中的雙下方法的詳細內容,更多請關注TW511.COM其它相關文章!