多繼承帶來的菱形問題

2022-08-18 04:45:19 字數 3763 閱讀 8843

大多數物件導向語言都不支援多繼承,而在python中,乙個子類是可以同時繼承多個父類的,這固然可以帶來乙個子類可以對多個不同父類加以重用的好處,但也有可能引發著名的 diamond problem菱形問題(或稱鑽石問題,有時候也被稱為「死亡鑽石」),菱形其實就是對下面這種繼承結構的形象比喻

這種繼承結構下導致的問題稱之為菱形問題:如果a中有乙個方法,b和/或c都重寫了該方法,而d沒有重寫它,那麼d繼承的是哪個版本的方法:b的還是c的?如下所示

class

a(object):

deftest(self):

print('from a')​​

class

b(a):

deftest(self):

print('from b')​​

class

c(a):

deftest(self):

print('from c')​​

class

d(b,c):

pass​​

obj = d()

obj.test()

# 結果為:from b

一、繼承原理(mro)

python到底是如何實現繼承的呢? 對於你定義的每乙個類,python都會計算出乙個方法解析順序(mro)列表,該mro列表就是乙個簡單的所有基類的線性順序列表,如下

>>>

d.mro() # 新式類內建了mro方法可以檢視線性列表的內容,經典類沒有該內建該方法

[<

class

'__main__.d'

>, <

class

'__main__.b'

>, <

class

'__main__.c'

>, <

class

'__main__.a'

>, <

class

'object'

>]

python會在mro列表上從左到右開始查詢基類,直到找到第乙個匹配這個屬性的類為止。 而這個mro列表的構造是通過乙個c3線性化演算法來實現的。我們不去深究這個演算法的數學原理,它實際上就是合併所有父類的mro列表並遵循如下三條準則:

1.

子類會先於父類被檢查

2.多個父類會根據它們在列表中的順序被檢查

3.如果對下乙個類存在兩個合法的選擇,

選擇第乙個父類

所以obj.test()的查詢順序是,先從物件obj本身的屬性裡找方法test,沒有找到,則參照屬性查詢的發起者(即obj)所處類d的mro列表來依次檢索,首先在類d中未找到,然後再b中找到方法test

1.

由物件發起的屬性查詢,會從物件自身的屬性裡檢索,沒有則會按照物件的類.mro()規定的順序依次找下去,

2.由類發起的屬性查詢,會按照當前類.mro()

規定的順序依次找下去,

二、深度和廣度的優先順序

非菱形結構:

參照下述**,多繼承結構為非菱形結構,此時,會按照先找b這一條分支,然後再找c這一條分支,最後找d這一條分支的順序直到找到我們想要的屬性

class

e:def

test(self):

print('from e')​​

class

f:def

test(self):

print('from f')​​

class

b(e):

deftest(self):

print('from b')​​

class

c(f):

deftest(self):

print('from c')​​

class

d:def

test(self):

print('from d')​​

class

a(b, c, d):

# def test(self):

#     print('from a')

pass​​

print(a.mro())

'''[, , , , , , ]

'''​

obj = a()

obj.test() # 結果為:from b

# 可依次注釋上述類中的方法test來進行驗證

菱形結構:

經典類————》python2

如果繼承關係為菱形結構,那麼經典類與新式類會有不同mro,分別對應屬性的兩種查詢方式:深度優先和廣度優先

class

g: # 在python2中,未繼承object的類及其子類,都是經典類

deftest(self):

print('from g')

​class

e(g):

deftest(self):

print('from e')

​class

f(g):

deftest(self):

print('from f')

​class

b(e):

deftest(self):

print('from b')

​class

c(f):

deftest(self):

print('from c')

​class

d(g):

deftest(self):

print('from d')

​class

a(b,c,d):

# def test(self):

#     print('from a')

pass

​obj = a()

obj.test() # 如上圖,查詢順序為:obj->a->b->e->g->c->f->d->object

# 可依次注釋上述類中的方法test來進行驗證,注意請在python2.x中進行測試

新式類————》python3

class

g(object):

deftest(self):

print('from g')

​class

e(g):

deftest(self):

print('from e')

​class

f(g):

deftest(self):

print('from f')

​class

b(e):

deftest(self):

print('from b')

​class

c(f):

deftest(self):

print('from c')

​class

d(g):

deftest(self):

print('from d')

​class

a(b,c,d):

# def test(self):

#     print('from a')

pass

​obj = a()

obj.test() # 如上圖,查詢順序為:obj->a->b->e->c->f->d->g->object

# 可依次注釋上述類中的方法test來進行驗證

總結:

多繼承到底要不用???

要用,但是規避幾點問題

1、繼承結構盡量不要過於複雜

2"是"

什麼的關係

多繼承與菱形繼承

乙個派生類繼承了兩個或多個基類,這樣的繼承關係叫做多繼承。include using namespace std class b class b1 class d public b,public b1 int main 如上 派生類d以public許可權繼承了基類b和基類b1。構造函式呼叫 首先呼叫...

繼承 多繼承 菱形虛擬繼承

繼承 繼承是由父類和子類 或稱之為基類與派生類 兩種角色構成,子承父業,就是說派生類會繼承基類的所有的成員,並擁有自己特有的成員。用一段 來解釋說明繼承關係 include using namespace std class base 基類 class derived public base 以共有...

單繼承 多繼承 菱形繼承

乙個類繼承另外乙個類,那麼該類就是子類 衍生類 被繼承的這個類叫做父類 基類,超類 繼承 1 單繼承 2 多繼承 python所有的類都預設繼承父類 object 如果子類想呼叫父類私有的,要先在父類裡面定義乙個公有的,然後在公有的裡面先調出私有,最後就可以間接的調出私有,不能在子類中定義公有 cl...