1. 程式人生 > >【Python】基礎-繼承和派生

【Python】基礎-繼承和派生

     面向物件的程式設計帶來的主要好處之一是程式碼的複用,實現這種複用的方法之一是通過繼承機制。
     python中解決程式碼複用的方式:繼承和組合。

1 什麼是繼承

     繼承是一種建立類的方法,在python中,一個類可以繼承來自一個或多個父類。原始類稱為基類或超類。

class ParentClass1: #定義父類
    pass

class ParentClass2: #定義父類
    pass

class SubClass1(ParentClass1): #單繼承,基類是ParentClass1,派生類是SubClass
    pass

class SubClass2
(ParentClass1,ParentClass2):
#python支援多繼承,用逗號分隔開多個繼承的類 pass

檢視繼承:

>>>SubClass2.__bases__
(<class'__main__.ParentClass1'>, <class'__main__.ParentClass2'>)

1.1 什麼時候用繼承

     假如已經有幾個類,而類與類之間有共同的變數屬性和函式屬性,那就可以把這幾個變數屬性和函式屬性提取出來作為基類的屬性。而特殊的變數屬性和函式屬性,則在本類中定義,這樣只需要繼承這個基類,就可以訪問基類的變數屬性和函式屬性。可以提高程式碼的可擴充套件性。

1.2 繼承和抽象(先抽象再繼承)

抽象即提取類似的部分。
基類就是抽象多個類共同的屬性得到的一個類。

class Riven:
    camp='Noxus'
    def__init__(self,nickname,
                 script,
                 aggressivity=54,
                 life_value=414,
                 ):

        self.nickname = nickname
        self.aggressivity = aggressivity
        self
.life_value = life_value self.script=script def attack(self,enemy): print(self.script) enemy.life_value -= self.aggressivity class Garen: camp='Demacia' def__init__(self,nickname, script, aggressivity=58, life_value=455, ): self.nickname = nickname self.aggressivity = aggressivity self.life_value = life_value self.script = script def attack(self,enemy): print(self.script) enemy.life_value -= self.aggressivity g1=Garen("德瑪西亞之力","人在塔在") g1.camp="諾克薩斯" r1=Riven("瑞雯","斷劍重鑄之日,騎士歸來之時") g1.attack(r1) print(r1.life_value)

結果:

人在塔在
356

Garen類和Riven類都有nickname、aggressivity、life_value、script四個變數屬性和attack()函式屬性,這裡可以抽象出一個Hero類,裡面有裡面包含這些屬性。

class Hero:
    def__init__(self, nickname,
                 script,
                 aggressivity,
                 life_value,
                 ):
        self.nickname = nickname
        self.aggressivity = aggressivity
        self.life_value = life_value
        self.script = script

    def attack(self,enemy):
        print("Hero.attack")
        enemy.life_value -= self.aggressivity

class Riven(Hero):
    camp='Noxus'
    def__init__(self,nickname,script,aggressivity=54,life_value=414):
        Hero.__init__(self,nickname,script,aggressivity,life_value)

    def attack(self,enemy):
        print(self.script)
        Hero.attack(self,enemy)

class Garen(Hero):
    camp='Demacia'
    def__init__(self,nickname,script,aggressivity=58,life_value=455):
        Hero.__init__(self, nickname, script, aggressivity, life_value)

    def attack(self,enemy):
        print(self.script)
        Hero.attack(self, enemy)

g1=Garen("德瑪西亞之力","人在塔在")
g1.camp="諾克薩斯"
r1=Riven("瑞雯","斷劍重鑄之日,騎士歸來之時")
g1.attack(r1)
print(r1.life_value)

結果:

人在塔在
Hero.attack
356

嚴格來說,上述Hero.init(self,...),不能算作子類呼叫父類的方法。因為我們如果去掉(Hero)這個繼承關係,程式碼仍能得到預期的結果。
總結python中繼承的特點:
1. 在子類中,並不會自動呼叫基類的__init__(),需要在派生類中手動呼叫。
2. 在呼叫基類的方法時,需要加上基類的類名字首,且需要帶上self引數變數。
3. 先在本類中查詢呼叫的方法,找不到才去基類中找。

1.3 繼承的好處

提高程式碼的複用。

1.4 繼承的弊端

     可能特殊的本類又有其他特殊的地方,又會定義一個類,其下也可能再定義類,這樣就會造成繼承的那條線越來越長,使用繼承的話,任何一點小的變化也需要重新定義一個類,很容易引起類的爆炸式增長,產生一大堆有著細微不同的子類. 所以有個“多用組合少用繼承”的原則。

2 什麼是派生

     派生就是子類在繼承父類的基礎上衍生出新的屬性。子類中獨有的,父類中沒有的;或子類定義與父類重名的東西。子類也叫派生類。

3 組合

程式碼複用的重要的方式除了繼承,還有組合。
組合,在一個類中以另一個類的物件作為資料屬性,稱為類的組合。

class Skill:
    def fire(self):
        print("release Fire skill")

class Riven:
    camp='Noxus'
    def__init__(self,nickname):
        self.nickname=nickname
        self.skill5=Skill().fire()#Skill類產生一個物件,並呼叫fire()方法,賦值給例項的skill5屬性

r1=Riven("瑞雯")

結果:

release Fire skill

4 組合和繼承的使用場景

  • 繼承的方式
    通過繼承建立了派生類與基類之間的關係,它是一種'是'的關係,比如白馬是馬,人是動物。
class Animal:
    def walk(self):
        print("Animal is walking")
    def eat(self):
        print("Animal is eating")

class Person(Animal):
    pass

p1=Person()
p1.walk()   #Animal is walking
  • 組合的方式
    用組合的方式建立了類與組合的類之間的關係,它是一種‘有’的關係,比如老師有生日,老師教python課程。
class Teacher:
    def__init__(self,name,sex,course):
        self.name=name
        self.sex=sex
        self.course = course

class Course:
    def__init__(self, name, period):
        self.name = name
        self.period = period

co=Course("python","7 m")
t1=Teacher("zhang","male",co)
print(t1.course.name)

結果:

python

當類之間有顯著不同,並且較小的類是較大的類所需要的元件時,用組合比較好。

5 介面和歸一化設計

什麼是介面?介面就是對外只提供抽象的方法的集合。

繼承的兩種用途:

  1. 繼承基類的方法,並且做出自己的改變或者擴充套件(程式碼複用)
  2. 宣告某個子類兼容於某基類,定義一個介面類Interface,介面類中定義了一些介面名(就是函式名)且並未實現介面的功能,子類繼承介面類,並且實現介面中的功能。
class Interface:#定義介面Interface類來模仿介面的概念,python中沒有interface關鍵字來定義一個介面。
    def read(self): #定介面函式read
        pass
        #raise AttributeError("read function must be implemented")    #如果不重寫read函式就報錯
        
    def write(self): #定義介面函式write
        pass
        #raise AttributeError("wirte function must be implemented")    #如果不重寫write函式就報錯
        
class Txt(Interface): #文字,具體實現read和write
    def read(self):
        print('文字資料的讀取方法')

    def write(self):
        print('文字資料的讀取方法')

class Sata(Interface): #磁碟,具體實現read和write
    def read(self):
        print('硬碟資料的讀取方法')

    def write(self):
        print('硬碟資料的讀取方法')

     實踐中,繼承的第一種含義意義並不很大,甚至常常是有害的。因為它使得子類與基類出現強耦合。
     繼承的第二種含義非常重要。它又叫“介面繼承”。
     介面繼承實質上是要求“做出一個良好的抽象,這個抽象規定了一個相容介面,使得外部呼叫者無需關心具體細節,可一視同仁的處理實現了特定介面的所有物件”——這在程式設計上,叫做歸一化。
     歸一化使得高層的外部使用者可以不加區分的處理所有介面相容的物件集合——就好象linux的泛檔案概念一樣,所有東西都可以當檔案處理,不必關心它是記憶體、磁碟、網路還是螢幕(當然,對底層設計者,當然也可以區分出“字元裝置”和“塊裝置”,然後做出針對性的設計:細緻到什麼程度,視需求而定)。

6 抽象類

6.1 什麼是抽象類

     與java一樣,python也有抽象類的概念,但是需要藉助模組實現。抽象類是一個特殊的類,它的特殊之處在於只能被繼承,不能被例項化。
     抽象類與普通類的不同之處在於:抽象類中只能有抽象方法(沒有實現功能),該類不能被例項化,只能被繼承,且子類必須實現抽象方法。

class Interface(metaclass=abc.ABCMeta):  # 定義介面Interface類來模仿介面的概念,python中沒有interface關鍵字來定義一個介面。
@abc.abstractclassmethod
    def read(self):  # 定介面函式read
        pass

@abc.abstractclassmethod
    def write(self):  # 定義介面函式write
        pass

class Txt(Interface):  # 文字,具體實現read和write
    def read(self):
        print('文字資料的讀取方法')

    def write(self):
        print('文字資料的讀取方法')

class Sata(Interface):  # 磁碟,具體實現read和write
    def read(self):
        print('硬碟資料的讀取方法')

    def write(self):
        print('硬碟資料的讀取方法')

6.2 抽象類與介面

     抽象類的本質還是類,抽象類的本質還是類,指的是一組類的相似性,包括資料屬性(如all_type)和函式屬性(如read、write),而介面只強調函式屬性的相似性。
     抽象類是一個介於類和介面直接的一個概念,同時具備類和介面的部分特性,可以用來實現歸一化設計 。