關於python究竟是如何實現繼承,能夠經過mro()來理解。首先定義多種繼承示例代碼:python
class A(object): def test(self): print('from A') class B(A): def test(self): print('from B') class C(A): def test(self): print('from C') class D(B): def test(self): print('from D') class E(C): def test(self): print('from E') class F(D,E): # def test(self): # print('from F') pass f1=F() f1.test() print(F.__mro__) #只有新式纔有這個屬性能夠查看線性列表,經典類沒有這個屬性 """ from D (<class '__main__.F'>, <class '__main__.D'>, <class '__main__.B'>, <class '__main__.E'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>) """
#新式類繼承順序:F->D->B->E->C->A 廣度優先
#經典類繼承順序:F->D->B->A->E->C 深度優先
#python3中統一都是新式類
#pyhon2中才分新式類與經典類
對於你定義的每個類,python會計算出一個方法解析順序(MRO)列表,這個MRO列表就是一個簡單的全部基類的線性順序列表。算法
F.mro()等同於上述示例代碼中的F.__mor__ide
python會在MRO列表上從左到右開始查找基類,直到找到第一個匹配這個屬性的類爲止。這個MRO列表的構造是經過一個C3線性化算法來實現,並遵循如下原則:函數
1)子類會先於父類被檢查spa
2)多個父類會根據它們在列表中的順序被檢查3d
3)若是對下一個類存在兩個合法的選擇,選擇第一個父類。code
在子類繼承多個父類時,屬性查找方式分深度優先和廣度優先兩種。blog
上圖中,A爲子類分別繼承B,C,D類,經典類按照深度優先方法查找。繼承
所以繼承順序是:B->E->G->C->F->D(python2中有經典類和新式類)
token
上圖中,A爲子類繼承B、C、D三類,新式類按照廣度優先方式查找。
新式類繼承順序:B->E->C->F->D->G
(python3中只有新式類)
在子類派生的新的方法中重用父類的方法,有兩種實現方式:
class Hero: def __init__(self, nickname, life_value, aggresivity): self.nickname = nickname self.life_value = life_value self.aggresivity = aggresivity def attack(self, enemy): enemy.life_value -= self.aggresivity class Garen(Hero): camp = 'Demacia' def attack(self, enemy): # 一旦從新定義了本身的屬性且與父類重名,調用新增屬性時,就以本身爲準。 Hero.attack(self, enemy) # 指名道姓,不依賴於繼承(一種重用方式) print('from Garen Class') class Riven(Hero): camp = 'Noxus' g = Garen('草叢倫', 100, 30) r = Riven('銳雯雯', 80, 50) print(r.life_value) g.attack(r) print(r.life_value) """ 80 from Garen Class 50 """
實例化子類添加本身獨有特徵__init__複用
class Hero: def __init__(self, nickname, life_value, aggresivity): self.nickname = nickname self.life_value = life_value self.aggresivity = aggresivity def attack(self, enemy): enemy.life_value -= self.aggresivity class Garen(Hero): camp = 'Demacia' def __init__(self, nickname, life_value, aggresivity, weapon): # 代碼複用 Hero.__init__(self, nickname, life_value, aggresivity) self.weapon = weapon def attack(self, enemy): Hero.attack(self, enemy) # 指名道姓 print('from Garen Class') g = Garen('草叢倫', 100, 30, '金箍棒') print(g.__dict__) """ {'nickname': '草叢倫', 'life_value': 100, 'aggresivity': 30, 'weapon': '金箍棒'} """
super() 函數是用於調用父類(超類)的一個方法。
super 是用來解決多重繼承問題的,直接用類名調用父類方法在使用單繼承的時候沒問題,可是若是使用多繼承,會涉及到查找順序(MRO)、重複調用(鑽石繼承)等種種問題。
MRO 就是類的方法解析順序表, 其實也就是繼承父類方法時的順序表。
super(type[, object-or-type]) 參數: type -- 類。 object-or-type -- 類,通常是 self
class Hero: def __init__(self, nickname, life_value, aggresivity): self.nickname = nickname self.life_value = life_value self.aggresivity = aggresivity def attack(self, enemy): enemy.life_value -= self.aggresivity class Garen(Hero): camp = 'Demacia' def attack(self, enemy): super(Garen, self).attack(enemy) # 依賴繼承 print('from Garen Class') class Riven(Hero): camp = 'Noxus' g = Garen('草叢倫', 100, 30) r = Riven('銳雯雯', 80, 50) g.attack(r) print(r.life_value) """ from Garen Class 50 """
class Hero: def __init__(self, nickname, life_value, aggresivity): self.nickname = nickname self.life_value = life_value self.aggresivity = aggresivity def attack(self, enemy): enemy.life_value -= self.aggresivity class Garen(Hero): camp = 'Demacia' def __init__(self, nickname, life_value, aggresivity, weapon): # 代碼複用 # Hero.__init__(self, nickname, life_value, aggresivity) # super(Garen, self).__init__(nickname, life_value, aggresivity) # python2必須這麼寫 super().__init__(nickname, life_value, aggresivity) # python3能夠這麼簡寫 self.weapon = weapon def attack(self, enemy): Hero.attack(self, enemy) # 指名道姓 print('from Garen Class') g = Garen('草叢倫', 100, 30, '金箍棒') print(g.__dict__) """ {'nickname': '草叢倫', 'life_value': 100, 'aggresivity': 30, 'weapon': '金箍棒'} """
指名道姓是跟繼承沒有關係,而方式二的super()是依賴於繼承的,而且即便沒有直接繼承關係,super仍然會按照mro繼續日後查找。
super與mro列表的關係
class A: def f1(self): print('from A') super().f1() # super無論A的繼承關係,按照C的MRO列表,繼續日後找:B class B: def f1(self): print('from B') class C(A,B): pass print(C.mro()) """ [<class '__main__.C'>, <class '__main__.A'>, <class '__main__.B'>, <class 'object'>] """ c = C() c.f1() """ from A from B """
當你使用super()函數時,Python會在MRO列表上繼續搜索下一個類。只要每一個重定義的方法統一使用super()並只調用它一次,那麼控制流最終會遍歷完整個MRO列表,每一個方法也只會被調用一次(注意注意注意:使用super調用的全部屬性,都是從MRO列表當前的位置日後找,千萬不要經過看代碼去找繼承關係,必定要看MRO列表)