pythonsuper多重继承_小白都能理解的Python多继承
前言
本文主要做科普用,在真實編程中不建議使用多重繼承,或者少用多重繼承,避免使代碼難以理解。
方法解析順序(MRO)
關于多重繼承,比較重要的是它的方法解析順序(可以理解為類的搜索順序),即MRO。這個跟類是新式類還是經典類有關,因為兩者的搜索算法不同。
在Python2及以前的版本,由任意內置類型派生出的類(只要一個內置類型位于類樹的某個位置),都屬于新式類;反之,不由任意內置類型派生出的類,則稱之為經典類
在Python3以后,沒有該區分,所有的類都派生自內置類型object,不管有沒有顯式繼承object,都屬于新式類。
對于經典類,多重繼承的MRO是深度優先,即從下往上搜索;新式類的MRO是采用C3算法(不同情況下,可表現為廣度優先,也可表現為深度優先)。
C3算法表現為深度優先的例子:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# C3-深度優先(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__':
# [, , , , ]
print(D.mro())
# A var
print(D.var)
復制代碼
C3算法表現為廣度優先的例子:
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-廣度優先(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__':
# [, , , , ]
print(D.mro())
# C var
print(D.var)
復制代碼
注:關于C3的詳細算法本文不討論,因為我也搞不懂(狗頭保命)
菱形多重繼承
其實菱形多重繼承上面已經有例子了,就是C3算法表現為廣度優先這個例子,畫起圖來是這樣的:
菱形多重繼承會導致的一個問題是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()
復制代碼
如果只想調用一次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()
復制代碼
_init_ 與 super()
1.如果父類有init方法,子類沒有,則子類默認繼承父類的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參數
bb = B("10", "100")
bb.say()
復制代碼
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參數,也沒有擁有a1,a2屬性
bb = B("10")
bb.say()
復制代碼
3.對于第二點,為了能使用或者擴展父類的行為,更常見的做法是在重寫init方法的同時,顯示調用父類的init方法(意味傳的參數要變成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)
# 第三種寫法:與前兩種等價,不過這種需要顯示調用基類,而第二種不用
# 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參數,也沒有擁有a1,a2屬性
bb = B("10", "100", "1000")
bb.say()
復制代碼
最后的提醒
注意 __init__ 方法不要寫錯,避免寫成__ini__或者其他的,因為兩個是在太像,出了問題很難排查(坑過兩次)。
參考
python 多重繼承的事
當心掉進Python多重繼承里的坑
總結
以上是生活随笔為你收集整理的pythonsuper多重继承_小白都能理解的Python多继承的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 高斯拟合原理_解读 | 得见的高斯过程
- 下一篇: 如何正确地做面条?