繼承原理、派生重用

1、繼承實現原理

  關於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中才分新式類與經典類

一、方法解析順序(MRO)列表 

  對於你定義的每個類,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中只有新式類)

2、子類重用父類方法及屬性

  在子類派生的新的方法中重用父類的方法,有兩種實現方式:

一、方式一:指名道姓,即父類名.父類方法()

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()  (依賴繼承)

(1)什麼是super?

  super() 函數是用於調用父類(超類)的一個方法。

  super 是用來解決多重繼承問題的,直接用類名調用父類方法在使用單繼承的時候沒問題,可是若是使用多繼承,會涉及到查找順序(MRO)、重複調用(鑽石繼承)等種種問題。

  MRO 就是類的方法解析順序表, 其實也就是繼承父類方法時的順序表。

(2)語法

super(type[, object-or-type])

參數:
  type -- 類。
  object-or-type -- 類,通常是 self

(3)應用示例

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
"""

(4)運用super()實例化子類添加獨有特徵

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()是依賴於繼承的,而且即便沒有直接繼承關係,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列表

相關文章
相關標籤/搜索