1. 程式人生 > >面向對象的繼承和派生

面向對象的繼承和派生

分別是 就是 %s 好處 auth 是否 hide 英雄 做成

一、繼承

1、什麽是繼承

繼承是一種創建新類的方式,新建的類可以繼承一個或多個父類(python支持多繼承),父類又可稱為基類或超類,新建的類稱為派生類或子類。

子類會“”遺傳”父類的屬性,從而解決代碼重用問題(比如練習7中Garen與Riven類有很多冗余的代碼)

2、python中類的繼承分為:單繼承和多繼承

技術分享圖片
class ParentClass1: #定義父類
    pass

class ParentClass2: #定義父類
    pass

class SubClass1(ParentClass1): #單繼承,基類是ParentClass1,派生類是SubClass
pass class SubClass2(ParentClass1,ParentClass2): #python支持多繼承,用逗號分隔開多個繼承的類 pass
View Code

3、查看繼承

技術分享圖片
>>> SubClass1.__bases__ #__base__只查看從左到右繼承的第一個子類,__bases__則是查看所有繼承的父類
(<class __main__.ParentClass1>,)
>>> SubClass2.__bases__
(<class __main__.ParentClass1
>, <class __main__.ParentClass2>)
View Code

4、經典類與新式類

技術分享圖片
1.只有在python2中才分新式類和經典類,python3中統一都是新式類
2.在python2中,沒有顯式的繼承object類的類,以及該類的子類,都是經典類
3.在python2中,顯式地聲明繼承object的類,以及該類的子類,都是新式類
3.在python3中,無論是否繼承object,都默認繼承object,即python3中所有類均為新式類
#關於新式類與經典類的區別,我們稍後討論
View Code

二、派生

當然子類也可以添加自己新的屬性或者在自己這裏重新定義這些屬性(不會影響到父類),需要註意的是,一旦重新定義了自己的屬性且與父類重名,那麽調用新增的屬性時,就以自己為準了。

class Riven(Hero):
    camp=Noxus
    def attack(self,enemy): #在自己這裏定義新的attack,不再使用父類的attack,且不會影響父類
        print(from riven)
    def fly(self): #在自己這裏定義新的
        print(%s is flying %self.nickname)

三、組合與重用性

組合:類與類之間沒有關聯,但又想把它們組合到一起

技術分享圖片
>>> class Equip: #武器裝備類
...     def fire(self):
...         print(release Fire skill)
... 
>>> class Riven: #英雄Riven的類,一個英雄需要有裝備,因而需要組合Equip類
...     camp=Noxus
...     def __init__(self,nickname):
...         self.nickname=nickname
...         self.equip=Equip() #用Equip類產生一個裝備,賦值給實例的equip屬性
... 
>>> r1=Riven(銳雯雯)
>>> r1.equip.fire() #可以使用組合的類產生的對象所持有的方法
release Fire skill
View Code

組合與繼承都是有效地利用已有類的資源的重要方式。但是二者的概念和使用場景皆不同

1.繼承的方式

通過繼承建立了派生類與基類之間的關系,它是一種‘是‘的關系,比如白馬是馬,人是動物。

當類之間有很多相同的功能,提取這些共同的功能做成基類,用繼承比較好,比如老師是人,學生是人

2.組合的方式

用組合的方式建立了類與組合的類之間的關系,它是一種‘有’的關系,比如教授有生日,教授教python和linux課程,教授有學生s1、s2、s3...

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

四、接口與歸一化設計

接口提取了一群類共同的函數,可以把接口當做一個函數的集合。然後讓子類去實現接口中的函數。

這麽做的意義在於歸一化,什麽叫歸一化,就是只要是基於同一個接口實現的類,那麽所有的這些類產生的對象在使用時,從用法上來說都一樣。

例如我們定義一個硬盤 ALLFile 的類,其他子類可以通過繼承它實現具體的功能。但是如果我們只是單純的寫成下面這樣:

class AllFile():
    def read(self):
        pass

    def write(self):
        pass


class Disk(AllFile):
    def read(self):
        print("read Disk")

disk = Disk()

這樣雖然我們可以繼承,但我們不用必須去實現父類所有的方法,普通的繼承達不到一種我們想要的約束的作用,這時改成

import abc

class
AllFile(metaclass=abc.ABCMeta): @abc.abstractmethod def read(self): pass @abc.abstractmethod def write(self): pass class Disk(AllFile): def read(self): print("read Disk") def write(self): print("write Disk") disk = Disk()

註意,父類只提供接口,具體實現邏輯在子類裏面

歸一化的好處在於:

1. 歸一化讓使用者無需關心對象的類是什麽,只需要的知道這些對象都具備某些功能就可以了,這極大地降低了使用者的使用難度。

2. 歸一化使得高層的外部使用者可以不加區分的處理所有接口兼容的對象集合

五、繼承實現的原理

1、子類查找父類的順序

分別是:深度優先和廣度優先

技術分享圖片

技術分享圖片

技術分享圖片
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__) #只有新式才有這個屬性可以查看線性列表,經典類沒有這個屬性

#新式類繼承順序:F->D->B->E->C->A
#經典類繼承順序:F->D->B->A->E->C
#python3中統一都是新式類
#pyhon2中才分新式類與經典類

繼承順序
繼承順序

2、繼承原理(python如何實現的繼承)

python到底是如何實現繼承的,對於你定義的每一個類,python會計算出一個方法解析順序(MRO)列表,這個MRO列表就是一個簡單的所有基類的線性順序列表,例如

>>> F.mro() #等同於F.__mro__
[<class __main__.F>, <class __main__.D>, <class __main__.B>, <class __main__.E>, <class __main__.C>, <class __main__.A>, <class object>]

為了實現繼承,python會在MRO列表上從左到右開始查找基類,直到找到第一個匹配這個屬性的類為止。

六、子類中調用父類的方法

方法一:即父類名.父類方法()

技術分享圖片
#_*_coding:utf-8_*_
__author__ = Linhaifeng

class Vehicle: #定義交通工具類
     Country=China
     def __init__(self,name,speed,load,power):
         self.name=name
         self.speed=speed
         self.load=load
         self.power=power

     def run(self):
         print(開動啦...)

class Subway(Vehicle): #地鐵
    def __init__(self,name,speed,load,power,line):
        Vehicle.__init__(self,name,speed,load,power)
        self.line=line

    def run(self):
        print(地鐵%s號線歡迎您 %self.line)
        Vehicle.run(self)

line13=Subway(中國地鐵,180m/s,1000人/箱,,13)
line13.run()
View Code

方法二:super()

技術分享圖片
class Vehicle: #定義交通工具類
     Country=China
     def __init__(self,name,speed,load,power):
         self.name=name
         self.speed=speed
         self.load=load
         self.power=power

     def run(self):
         print(開動啦...)

class Subway(Vehicle): #地鐵
    def __init__(self,name,speed,load,power,line):
        #super(Subway,self) 就相當於實例本身 在python3中super()等同於super(Subway,self)
        super().__init__(name,speed,load,power)
        self.line=line

    def run(self):
        print(地鐵%s號線歡迎您 %self.line)
        super(Subway,self).run()

class Mobike(Vehicle):#摩拜單車
    pass

line13=Subway(中國地鐵,180m/s,1000人/箱,,13)
line13.run()
View Code

註意:super()調用的時候不用傳self

註意:即使沒有直接繼承關系,super仍然會按照mro繼續往後查找

技術分享圖片
#A沒有繼承B,但是A內super會基於C.mro()繼續往後找
class A:
    def test(self):
        super().test()
class B:
    def test(self):
        print(from B)
class C(A,B):
    pass

c=C()
c.test() #打印結果:from B


print(C.mro())
#[<class ‘__main__.C‘>, <class ‘__main__.A‘>, <class ‘__main__.B‘>, <class ‘object‘>]
View Code

面向對象的繼承和派生