一区二区三区在线-一区二区三区亚洲视频-一区二区三区亚洲-一区二区三区午夜-一区二区三区四区在线视频-一区二区三区四区在线免费观看

腳本之家,腳本語言編程技術(shù)及教程分享平臺!
分類導(dǎo)航

Python|VBS|Ruby|Lua|perl|VBA|Golang|PowerShell|Erlang|autoit|Dos|bat|

服務(wù)器之家 - 腳本之家 - Python - 小白都能理解的Python多繼承

小白都能理解的Python多繼承

2020-10-17 23:56言淦 Python

本文主要做科普用,在真實編程中不建議使用Python多重繼承,或者少用多重繼承,避免使代碼難以理解。

前言

本文主要做科普用,在真實編程中不建議使用多重繼承,或者少用多重繼承,避免使代碼難以理解。

方法解析順序(MRO)

 

關(guān)于多重繼承,比較重要的是它的方法解析順序(可以理解為類的搜索順序),即MRO。這個跟類是新式類還是經(jīng)典類有關(guān),因為兩者的搜索算法不同。

Python2及以前的版本,由任意內(nèi)置類型派生出的類(只要一個內(nèi)置類型位于類樹的某個位置),都屬于新式類;反之,不由任意內(nèi)置類型派生出的類,則稱之為經(jīng)典類

在Python3以后,沒有該區(qū)分,所有的類都派生自內(nèi)置類型object,不管有沒有顯式繼承object,都屬于新式類

對于經(jīng)典類,多重繼承的MRO是深度優(yōu)先,即從下往上搜索;新式類的MRO是采用C3算法(不同情況下,可表現(xiàn)為廣度優(yōu)先,也可表現(xiàn)為深度優(yōu)先)

C3算法表現(xiàn)為深度優(yōu)先的例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# C3-深度優(yōu)先(D -> B -> A -> C)
class A:
    var = 'A var'
 
 
class B(A):
    pass
 
 
class C:
    var = 'C var'
 
 
class D(B, C):
    pass
 
 
if __name__ == '__main__':
    # [<class '__main__.D'>, <class '__main__.B'>, <class '__main__.A'>, <class '__main__.C'>, <class 'object'>]
    print(D.mro())
    # A var
    print(D.var)
復(fù)制代碼

C3算法表現(xiàn)為廣度優(yōu)先的例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# C3-廣度優(yōu)先(D -> B -> C -> A)
class A:
    var = 'A var'
 
 
class B(A):
    pass
 
 
class C(A):
    var = 'C var'
 
 
class D(B, C):
    pass
 
 
if __name__ == '__main__':
    # [<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>]
    print(D.mro())
    # C var
    print(D.var)
 
復(fù)制代碼

注:關(guān)于C3的詳細(xì)算法本文不討論,因為我也搞不懂(狗頭保命)

菱形多重繼承

 

其實菱形多重繼承上面已經(jīng)有例子了,就是C3算法表現(xiàn)為廣度優(yōu)先這個例子,畫起圖來是這樣的:

小白都能理解的Python多繼承

 

菱形多重繼承會導(dǎo)致的一個問題是A初始化了兩次,如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
class A:
    def say(self):
        print("A say")
 
 
class B(A):
    def say(self):
        print("B say")
        A.say(self)
 
 
class C(A):
    def say(self):
        print("C say")
        A.say(self)
 
 
class D(B, C):
    def say(self):
        print("D say")
        B.say(self)
        C.say(self)
 
 
if __name__ == '__main__':
    dd = D()
    dd.say()
 
復(fù)制代碼

如果只想調(diào)用一次A,可使用super方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
class A:
    def say(self):
        print("A say")
 
 
class B(A):
    def say(self):
        print("B say")
        super().say()
 
 
class C(A):
    def say(self):
        print("C say")
        super().say()
 
 
class D(B, C):
    def say(self):
        print("D say")
        super().say()
 
 
if __name__ == '__main__':
    print(D.mro())
    dd = D()
    dd.say()
 
復(fù)制代碼

_init_ 與 super()

 

1.如果父類有init方法,子類沒有,則子類默認(rèn)繼承父類的init方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class A:
    def __init__(self, a1, a2):
        self.a1 = a1
        self.a2 = a2
 
    def say(self):
        print("A say, a1: %s, a2: %s" % (self.a1, self.a2))
 
 
class B(A):
    def say(self):
        print("B say, a1: %s, a2: %s" % (self.a1, self.a2))
 
 
if __name__ == '__main__':
    # 因為B繼承了A的init方法,所以也要傳入 a1,a2參數(shù)
    bb = B("10", "100")
    bb.say()
 
復(fù)制代碼

2.如果父類有init方法,子類也有,可理解為子類重寫了父類的init方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class A:
    def __init__(self, a1, a2):
        self.a1 = a1
        self.a2 = a2
 
    def say(self):
        print("A say, a1: %s, a2: %s" % (self.a1, self.a2))
 
 
class B(A):
    def __init__(self, b1):
        self.b1 = b1
 
    def say(self):
        print("B say, b1: %s" % self.b1)
 
 
if __name__ == '__main__':
    # 此處B重寫了A的init方法,所以只需要傳入b1參數(shù),也沒有擁有a1,a2屬性
    bb = B("10")
    bb.say()
 
復(fù)制代碼

3.對于第二點,為了能使用或者擴展父類的行為,更常見的做法是在重寫init方法的同時,顯示調(diào)用父類的init方法(意味傳的參數(shù)要變成3個)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
# 三種寫法
class A:
    def __init__(self, a1, a2):
        self.a1 = a1
        self.a2 = a2
 
    def say(self):
        print("A say, a1: %s, a2: %s" % (self.a1, self.a2))
 
 
class B(A):
    def __init__(self, b1, a1, a2):
        # 第一種寫法: Python2的寫法
        # super(B, self).__init__(a1, a2)
        # 第二種寫法(推薦):Python3的寫法,與第一種等價
        super().__init__(a1, a2)
        # 第三種寫法:與前兩種等價,不過這種需要顯示調(diào)用基類,而第二種不用
        # A.__init__(self, a1, a2)
        self.b1 = b1
 
    def say(self):
        print("B say, a1: %s, a2: %s, b1: %s" % (self.a1, self.a2, self.b1))
 
 
if __name__ == '__main__':
    # 此處B重寫了A的init方法,所以只需要傳入b1參數(shù),也沒有擁有a1,a2屬性
    bb = B("10", "100", "1000")
    bb.say()
復(fù)制代碼

最后的提醒

注意 __init__ 方法不要寫錯,避免寫成__ini__或者其他的,因為兩個是在太像,出了問題很難排查(坑過兩次)。

參考

python 多重繼承的事
當(dāng)心掉進Python多重繼承里的坑

延伸 · 閱讀

精彩推薦
主站蜘蛛池模板: 国产免费资源 | 久久中文字幕综合不卡一二区 | 全肉np巨肉一女np高h双龙 | 久久re6热在线视频 久久AV喷吹AV高潮欧美 | 国产精品久久免费观看 | 日本一卡二卡3卡四卡网站精品 | 色综合中文字幕在线亚洲 | 欧美三级免费观看 | 丝袜足控免费网站xx动漫漫画 | www视频免费 | 2020国语对白露脸 | 草综合| 国产成人无精品久久久 | 成人午夜视频一区二区国语 | 日韩欧美不卡片 | 久久国产乱子伦免费精品 | 护士xxxx| 万域之王动漫在线观看全集免费播放 | 天天做天天玩天天爽天天 | 日本中文字幕一区二区有码在线 | 草留色区| kk4kk免费毛片| 亚洲国产精久久久久久久 | 国产成人一区二区三区视频免费蜜 | 日韩精品亚洲一级在线观看 | 日本成人高清视频 | 日本春菜花在线中文字幕 | 啪啪艹| 亚洲欧美成人综合久久久 | 日本十大顶级绝伦推理片 | 日韩精品亚洲一级在线观看 | 久久re热在线视频精69 | 精品欧美一区二区三区久久久 | 国产激情视频网站 | 999导航| 国色天香高清版 | 天天做日日做天天添天天欢公交车 | 国内精品伊人久久大香线焦 | 日韩欧美一区二区三区免费观看 | 青青草国产免费国产是公开 | 亚洲乱码一二三四五六区 |