天天看點

python面向對象---03-繼承、多繼承、super

python面向對象—03-繼承、多繼承、super

階段一:繼承

情景模式:假設你正在參與一個魔幻類角色遊戲的開發,公司需要你為這個遊戲設計兩個角色的類:

  1. 劍士
    • 具有如下屬性:
      • 角色名
      • 角色等級
      • 生命值
      • 攻擊力
    • 具有如下行為:
      • 實體攻擊
  2. 法師
    • 具有如下屬性:
      • 角色名
      • 角色等級
      • 生命值
      • 攻擊力
    • 具有如下行為:
      • 實體攻擊

代碼實作:

英勇的劍士:

class SwordsMan:
    def __init__(self, name, level, blood, attack_power):
        '''
        :param name:角色姓名
        :param level:角色等級
        :param blood:角色血量
        '''
        self.name = name
        self.level = level
        self.blood = blood
        self.power = attack_power

    def fight(self):   #角色攻擊行為
        print('實體攻擊')
    def __str__(self):
        return "{clc}({name},{level},{blood})".format(clc = self.__class__.__name__,
                                                      name = self.name,
                                                      level = self.level,
                                                      blood = self.blood)
           

智慧的法師:

class Magician:
    def __init__(self, name, level, blood, magic_power):
        '''
        :param name:角色姓名
        :param level:角色等級
        :param blood:角色血量
        '''
        self.name = name
        self.level = level
        self.blood = blood
        self.power = magic_power
    def fight(self):   #角色攻擊行為
        print('魔法攻擊')
    def __str__(self):
        return "{clc}({name},{level},{blood})".format(clc = self.__class__.__name__,
                                                      name = self.name,
                                                      level = self.level,
                                                      blood = self.blood)
           

繼承 問題引入

問題一: 兩個中有大量重複的代碼,是否能夠隻寫一次 ?

  • 抽象出一個更抽象的類,放公共代碼

問題二: 繼承的意義是什麼 ?

  • 重用代碼,友善代碼的管理和修改

問題三: 繼承是複制變量空間嘛 ?

  • 繼承不是變量空間複制

分類關系圖

python面向對象---03-繼承、多繼承、super

對以上連個角色進行進一步抽象,定義一個更加抽象的類— 角色類

代碼如下:

class Rloe:
    def __init__(self,name,level,blood):
        '''
        :param name:角色姓名
        :param level:角色等級
        :param blood:角色血量
        '''
        self.name = name
        self.level =level
        self.blood = blood

    def __str__(self):
        return "{clc}({name},{level},{blood})".format(clc = self.__class__.__name__,
                                                      name = self.name,
                                                      level = self.level,
                                                      blood = self.blood)

    def fight(self):  # 角色攻擊行為
        raise NotImplementedError('必須在子類中實作該行為')   
           

繼承關系圖

python面向對象---03-繼承、多繼承、super

再從角色類中派生具體的類

注意:(派生與繼承是一個意思的兩個說法)

派生出的劍士:

class SwordsMan(Rloe):     #繼承自Role
    def __init__(self,name,level,blood,attack_power):
        '''
        :param name:角色姓名
        :param level:角色等級
        :param blood:角色血量
        '''
        Rloe.__init__(self,name,level,blood)
        self.attack_power = attack_power
    def fight(self):   #角色攻擊行為
        print('實體攻擊')
           

派生出的法師:

class Magician:        #繼承自Role
    def __init__(self,name,level,blood,magic_power):
        '''
        :param name:角色姓名
        :param level:角色等級
        :param blood:角色血量
        '''
        Rloe.__init__(self, name, level, blood)
        self.attack_power = attack_power
    def fight(self):   #角色攻擊行為
        print('魔法攻擊')
    def cure(self):  # 角色技能
        print('治療')
           

繼承搜尋

搜尋原則:(通路類的屬性或方法)

  • 如果找不到轉到其父類中查找
  • 如果再找不到轉到其父類的父類中查找

執行個體:

class BaseClass:
    attritube = 
    def method(self):
        pass
class DeriverClass:    #簡單繼承,沒有任何的封裝,重寫
    pass
print(DeriverClass.__dict__)  #DeriverClass中沒有attritube與method
print(BaseClass.__dict__)     #BaseClass中有
           

重用父類的 _ init_

左邊的self 是 劍士(子類)的執行個體 右邊的self是也是劍士的執行個體

python面向對象---03-繼承、多繼承、super

頂級基類 object

class ClassName:
    pass
class ClassN(object):
    pass
print(ClassName.__bases__)
print(ClassN.__bases__)
           

思考:為什麼傳回的是一個元祖 ?

  • 有可能繼承多個類(及多繼承),以元組顯示。

階段二:多重繼承

多重繼承 問題引入

問題一: 一個類隻能有一個基類嘛 ?

問題二: 如果有多個基類,如果有同名屬性和方法,應該如何選擇?

問題三: 有沒有一種更加優雅的方式調用父類的方法 ?

繼承關系圖:

python面向對象---03-繼承、多繼承、super

代碼實作如下:

class D:
    pass
class E:
    pass
class C:
    pass
class B(D,E):
    pass
class A(B,C):
    pass

print(A.mro())
           

結果輸出:

[<class '__main__.A'>, <class '__main__.B'>, <class '__main__.D'>, <class '__main__.E'>, <class '__main__.C'>, <class 'object'>]

程序已結束,退出代碼0
           

MRO-C3 搜尋分析

S[D] = <D O>
S[E] = <E O>
S[C] = <C O>

S[B] = B + merge(S[D], S[E], <D E>)
        = B + merge(<D O>, <E O>, <D E>)
        = <B D> + merge(O, <E O>, E)
        = <B D E O>

S[A] = A + merge(S[B], S[C], <B C>)
        = A + merge(<B D E O>, <C O>, <B C>)
        = <A B> + merge(<D E O>, <C O>, C)
        = <A B D> + merge(<E O>, <C O>, C)
        = <A B D E> + merge(O, <C O>, C)
        = <A B D E C O>
           

由于沖突導緻的不能繼承

python面向對象---03-繼承、多繼承、super

代碼實作如下:

class A:
    pass
class B:
    pass
class C(A,B):
    pass
class D(B,A):
    pass
class E(C,D):
    pass

print(E.mro())
           

結果輸出:

Traceback (most recent call last):
  File "G:/xxx/xxx/xxx/xxx/TTT.py", line , in <module>
    class E(C,D):
TypeError: Cannot create a consistent method resolution
order (MRO) for bases A, B
           

MRO-C3 搜尋分析

S[A] = <A O>
S[B] = <B O>

S[C] = C + merge(S[A], S[B], <A B>)
        = C + merge(<A O>, <B O>, <A B>)
        = <C A> + merge(O, <B O>, <B>)
        = <C A B O>
S[D] = <D B A O>

S[E] = E + merge(S[C], S[D], <C D>)
        = E + merge(<C A B O>, <D B A O>, <CD>)
        = <E C> + merge(<A B O>, <D B A O>, <D>)
        = <E C D> + merge(<A B O>, <B A O>)    # 沖突
           

鴨子類型

執行個體:

def draw_fight(role):
  print('role',end='')
  role.fight()    #其實并不關心,role是不是一個執行個體
class SwordsMan:
    def __init__(self, name, level, blood, attack_power):
        '''
        :param name:角色姓名
        :param level:角色等級
        :param blood:角色血量
        '''
        self.name = name
        self.level = level
        self.blood = blood
        self.power = attack_power

    def fight(self):   #角色攻擊行為
        print('實體攻擊')
    def __str__(self):
        return "{clc}({name},{level},{blood})".format(clc = self.__class__.__name__,
                                                      name = self.name,
                                                      level = self.level,
                                                      blood = self.blood)
class Magician:
    def __init__(self, name, level, blood, magic_power):
        '''
        :param name:角色姓名
        :param level:角色等級
        :param blood:角色血量
        '''
        self.name = name
        self.level = level
        self.blood = blood
        self.power = magic_power
    def fight(self):   #角色攻擊行為
        print('魔法攻擊')
    def __str__(self):
        return "{clc}({name},{level},{blood})".format(clc = self.__class__.__name__,
                                                      name = self.name,
                                                      level = self.level,
                                                      blood = self.blood)

s = SwordsMan('lili',,,)
m = Magician('haha',,,)
draw_fight(s)  #列印出:role實體攻擊
draw_fight(m)  #列印出:role魔法攻擊
           

如果走起路來像鴨子,或者叫起來像鴨子,那就是鴨子!

基于多繼承的 Mix-in 設計模式

python面向對象---03-繼承、多繼承、super

原則:

  • 最好,多繼承就一層,且是最後一層
  • 注意:一般,“Mix-in類”是繼承的終點 !

階段三:super 内置函數

如何更加優雅的調用父類中方法?

代碼實作如下:

劍士super方法改進:

class SwordsMan(Rloe):     #繼承自Role
    def __init__(self,name,level,blood,attack_power):
        '''
        :param name:角色姓名
        :param level:角色等級
        :param blood:角色血量
        '''
        super().__init__(name,level,blood)
        self.attack_power = attack_power
    def fight(self):   #角色攻擊行為
        print('實體攻擊')
           

法師劍士super方法改進:

class Magician:        #繼承自Role
    def __init__(self,name,level,blood,magic_power):
        '''
        :param name:角色姓名
        :param level:角色等級
        :param blood:角色血量
        '''
        super().__init__(name, level, blood)
        self.attack_power = attack_power
    def fight(self):   #角色攻擊行為
        print('魔法攻擊')
    def cure(self):  # 角色技能
        print('治療')
           

super不需要再傳self、super是通過mro查找的

練習:

eg:

假設你正在參與潭州教育公司的軟體學員管理系統開發,需要你為系統設計學員類:潭州教育軟體類别:

  1. C/C++
  2. Web前端
  3. Python

請設計一個基類表示學員,然後使用繼承來實作各科目的軟體學員。

代碼實作如下:

class Student:
    def __init__(self,name,age,sex):
        '''
        :param name:學員姓名
        :param age:學員年齡
        :param sex:學員性别
        '''
        self.name = name
        self.age =age
        self.sex = sex
    def __str__(self):
        return "{clc}({name},{age},{sex})".format(clc = self.__class__.__name__,
                                                      name = self.name,
                                                      age = self.age,
                                                      sex = self.sex)

    def learn(self):  # 學員學習課程
        raise NotImplementedError('必須在子類中實作該方法')

class C(Student):     #繼承自Student
    def __init__(self,name,age,sex,lesson):
        '''
         :param name:學員姓名
        :param age:學員年齡
        :param sex:學員性别
        '''
        super().__init__(name,age,sex)
        self.lesson = lesson
    def learn(self):   #學習課程
        print('學習C/C++課程')

class Web(Student):         #繼承自Student
    def __init__(self,name,age,sex,lesson):
        '''
         :param name:學員姓名
        :param age:學員年齡
        :param sex:學員性别
        '''
        super().__init__(name,age,sex)
        self.lesson = lesson
    def learn(self):   #學習課程
        print('學習Web前端課程')

class Python(Student):         #繼承自Student
    def __init__(self,name,age,sex,lesson):
        '''
         :param name:學員姓名
        :param age:學員年齡
        :param sex:學員性别
        '''
        super().__init__(name,age,sex)
        self.lesson = lesson
    def learn(self):   #學習課程
        print('學習Python課程')
c= C('lili',,'M','c/c++')
w= Web('haha',,'F','Web前端')   
p= Python('dada',,'M','Python')
print(c)
c.learn()
print(w)
w.learn()
print(p)
p.learn()
           

輸出結果:

C(lili,,M)
學習C/C++課程
Web(haha,,F)
學習Web前端課程
Python(dada,,M)
學習Python課程
>>>