Python面向對象編程——繼承與派生

Python面向對象編程——繼承與派生

1、初始繼承

一、什麼是繼承python

繼承指的是類與類之間的關係,是一種什麼「是」什麼的關係,繼承的功能之一就是用來解決代碼重用問題。算法

繼承是一種建立新類的方式,在python中,新建的類能夠繼承一個或多個父類,父類又能夠成爲基類或超類,新建的類稱爲派生類或子類編程

二、python中類的繼承分爲:單繼承和多繼承編程語言

class ParentClass1: #定義父類
    pass

class ParentClass2: #定義父類
    pass

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

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

三、查看繼承函數

>>> SubClass1.__bases__ #__base__只查看從左到右繼承的第一個子類,__bases__則是查看全部繼承的父類
(<class '__main__.ParentClass1'>,)
>>> SubClass2.__bases__
(<class '__main__.ParentClass1'>, <class '__main__.ParentClass2'>)

四、經典類與新式類工具

1.只有在python2中才分新式類和經典類,python3中統一都是新式類
2.在python2中,沒有顯式的繼承object類的類,以及該類的子類,都是經典類
3.在python2中,顯式地聲明繼承object的類,以及該類的子類,都是新式類
4.在python3中,不管是否繼承object,都默認繼承object,即python3中全部類均爲新式類

注:若是沒有指定基類,python的類會默認繼承object類,object是全部python類的基類,它提供了一些常見方法(如__str__)的實現。設計

>>> ParentClass1.__bases__
(<class 'object'>,)
>>> ParentClass2.__bases__
(<class 'object'>,)

2、繼承與抽象(先抽象再繼承)

抽象即抽取相似或者說比較像的部分。3d

抽象分紅兩個層次:code

一、將奧巴馬和梅西這倆對象比較像的部分抽取成類;對象

二、將人,豬,狗這三個類比較像的部分抽取成父類。

抽象最主要的做用是劃分類別(能夠隔離關注點,下降複雜度)

繼承:是基於抽象的結果,經過編程語言去實現它,確定是先經歷抽象這個過程,才能經過繼承的方式去表達出抽象的結構。

抽象只是分析和設計的過程當中,一個動做或者說一種技巧,經過抽象能夠獲得類。

3、繼承與重用性

在開發程序的過程當中,若是咱們定義了一個類A,而後又想新創建另一個類B,可是類B的大部份內容與類A的相同時,咱們不可能從頭開始寫一個類B,這就用到了類的繼承的概念。

經過繼承的方式新建類B,讓B繼承A,B會‘遺傳’A的全部屬性(數據屬性和函數屬性),實現代碼重用。

class Hero:
    def __init__(self,nickname,aggressivity,life_value):
        self.nickname=nickname
        self.aggressivity=aggressivity
        self.life_value=life_value

    def move_forward(self):
        print('%s move forward' %self.nickname)

    def move_backward(self):
        print('%s move backward' %self.nickname)

    def move_left(self):
        print('%s move forward' %self.nickname)

    def move_right(self):
        print('%s move forward' %self.nickname)

    def attack(self,enemy):
        enemy.life_value-=self.aggressivity
class Garen(Hero):
    pass

class Riven(Hero):
    pass

g1=Garen('草叢倫',100,300)
r1=Riven('銳雯雯',57,200)

print(g1.life_value) #結果:300
r1.attack(g1)
print(g1.life_value) #結果:243

注:用已經有的類創建一個新的類,這樣就重用了已經有的軟件中的一部分設置大部分,大大節省了編程工做量,這就是常說的軟件重用,不只能夠重用本身的類,也能夠繼承別人的,好比標準庫,來定製新的數據類型,這樣就是大大縮短了軟件開發週期,對大型軟件開發來講,意義重大。

4、再看屬性查找

像g1.life_value之類的屬性引用,會先從實例中找life_value而後去類中找,而後再去父類中找...直到最頂級的父類。那麼如何解釋下面的打印結果呢?

class Foo:
    def f1(self):
        print('Foo.f1')

    def f2(self):
        print('Foo.f2')
        self.f1()

class Bar(Foo):
    def f1(self):
        print('Bar.f1')


b=Bar()
b.f2()

# 打印結果:
# Foo.f2
# Bar.f1

5、派生

固然子類也能夠添加本身新的屬性或者在本身這裏從新定義這些屬性(不會影響到父類),須要注意的是,一旦從新定義了本身的屬性且與父類重名,那麼調用新增的屬性時,就以本身爲準了。

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

在子類中,新建的重名的函數屬性,在編輯函數內功能的時候,有可能須要重用父類中重名的那個函數功能,應該是用調用普通函數的方式,即:類名.func(),此時就與調用普通函數無異了,所以即使是self參數也要爲其傳值。

class Riven(Hero):
    camp='Noxus'
    def __init__(self,nickname,aggressivity,life_value,skin):
        Hero.__init__(self,nickname,aggressivity,life_value) #調用父類功能
        self.skin=skin #新屬性
    def attack(self,enemy): #在本身這裏定義新的attack,再也不使用父類的attack,且不會影響父類
        Hero.attack(self,enemy) #調用功能
        print('from riven')
    def fly(self): #在本身這裏定義新的
        print('%s is flying' %self.nickname)

r1=Riven('銳雯雯',57,200,'比基尼')
r1.fly()
print(r1.skin)

'''
運行結果
銳雯雯 is flying
比基尼
'''

6、繼承的實現與原理

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列表上從左到右開始查找基類,直到找到第一個匹配這個屬性的類爲止。而這個MRO列表的構造是經過一個C3線性化算法來實現的。咱們不去深究這個算法的數學原理,它實際上就是合併全部父類的MRO列表並遵循以下三條準則:

一、子類會先於父類被檢查。

二、多個父類會根據它們在列表中的順序被檢查。

三、若是對下一個類存在兩個合法的選擇,選擇第一個父類。

在Java和C#中子類只能繼承一個父類,而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__) #只有新式纔有這個屬性能夠查看線性列表,經典類沒有這個屬性

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

7、在子類中調用父類的方法

在子類派生出的新方法中,每每須要重用父類的方法,咱們有兩種方式實現

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

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()

方式二: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()

這兩種方式的區別是:方式一是跟繼承沒有關係的,而方式二的super()是依賴於繼承的,而且即便沒有直接繼承關係,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'>]
相關文章
相關標籤/搜索