Python面向?qū)ο缶幊獭^承與派生
一、初始繼承
1、什么是繼承
繼承指的是類與類之間的關(guān)系,是一種
什么“是”什么
的關(guān)系,繼承的功能之一就是用來解決代碼重用問題。
繼承是一種創(chuàng)建新類的方式,在python中,新建的類可以繼承一個或多個父類,父類又可以成為基類或超類,新建的類稱為派生類或子類
2、python中類的繼承分為:單繼承和多繼承
class ParentClass1: #定義父類
pass
class ParentClass2: #定義父類
pass
class SubClass1(ParentClass1): #單繼承,基類是ParentClass1,派生類是SubClass
pass
class SubClass2(ParentClass1,ParentClass2): #python支持多繼承,用逗號分隔開多個繼承的類
pass
3、查看繼承
>>> SubClass1.__bases__ #__base__只查看從左到右繼承的第一個子類,__bases__則是查看所有繼承的父類
(
,)
>>> SubClass2.__bases__
(
,
)
4、經(jīng)典類與新式類
1.只有在python2中才分新式類和經(jīng)典類,python3中統(tǒng)一都是新式類
2.在python2中,沒有顯式的繼承object類的類,以及該類的子類,都是經(jīng)典類
3.在python2中,顯式地聲明繼承object的類,以及該類的子類,都是新式類
4.在python3中,無論是否繼承object,都默認(rèn)繼承object,即python3中所有類均為新式類
注:如果沒有指定基類,python的類會默認(rèn)繼承object類,object是所有python類的基類,它提供了一些常見方法(如
__str__
)的實現(xiàn)。
>>> ParentClass1.__bases__
(
,)
>>> ParentClass2.__bases__
(
,)
二、繼承與抽象(先抽象再繼承)
抽象即抽取類似或者說比較像的部分。
抽象分成兩個層次:
1、將奧巴馬和梅西這倆對象比較像的部分抽取成類;
2、將人,豬,狗這三個類比較像的部分抽取成父類。
抽象最主要的作用是劃分類別(可以隔離關(guān)注點,降低復(fù)雜度)
繼承:是基于抽象的結(jié)果,通過編程語言去實現(xiàn)它,肯定是先經(jīng)歷抽象這個過程,才能通過繼承的方式去表達(dá)出抽象的結(jié)構(gòu)。
抽象只是分析和設(shè)計的過程中,一個動作或者說一種技巧,通過抽象可以得到類。
三、繼承與重用性
在開發(fā)程序的過程中,如果我們定義了一個類A,然后又想新建立另外一個類B,但是類B的大部分內(nèi)容與類A的相同時,我們不可能從頭開始寫一個類B,這就用到了類的繼承的概念。
通過繼承的方式新建類B,讓B繼承A,B會‘遺傳’A的所有屬性(數(shù)據(jù)屬性和函數(shù)屬性),實現(xiàn)代碼重用。
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) #結(jié)果:300
r1.attack(g1)
print(g1.life_value) #結(jié)果:243
注:用已經(jīng)有的類建立一個新的類,這樣就重用了已經(jīng)有的軟件中的一部分設(shè)置大部分,大大節(jié)省了編程工作量,這就是常說的軟件重用,不僅可以重用自己的類,也可以繼承別人的,比如標(biāo)準(zhǔn)庫,來定制新的數(shù)據(jù)類型,這樣就是大大縮短了軟件開發(fā)周期,對大型軟件開發(fā)來說,意義重大。
四、再看屬性查找
像g1.life_value之類的屬性引用,會先從實例中找life_value然后去類中找,然后再去父類中找...直到最頂級的父類。那么如何解釋下面的打印結(jié)果呢?
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()
# 打印結(jié)果:
# Foo.f2
# Bar.f1
五、派生
當(dāng)然子類也可以添加自己新的屬性或者在自己這里重新定義這些屬性(不會影響到父類),需要注意的是,一旦重新定義了自己的屬性且與父類重名,那么調(diào)用新增的屬性時,就以自己為準(zhǔn)了。
class Riven(Hero):
camp='Noxus'
def attack(self,enemy): #在自己這里定義新的attack,不再使用父類的attack,且不會影響父類
print('from riven')
def fly(self): #在自己這里定義新的
print('%s is flying' %self.nickname)
在子類中,新建的重名的函數(shù)屬性,在編輯函數(shù)內(nèi)功能的時候,有可能需要重用父類中重名的那個函數(shù)功能,應(yīng)該是用調(diào)用普通函數(shù)的方式,即:類名.func(),此時就與調(diào)用普通函數(shù)無異了,因此即便是self參數(shù)也要為其傳值。
class Riven(Hero):
camp='Noxus'
def __init__(self,nickname,aggressivity,life_value,skin):
Hero.__init__(self,nickname,aggressivity,life_value) #調(diào)用父類功能
self.skin=skin #新屬性
def attack(self,enemy): #在自己這里定義新的attack,不再使用父類的attack,且不會影響父類
Hero.attack(self,enemy) #調(diào)用功能
print('from riven')
def fly(self): #在自己這里定義新的
print('%s is flying' %self.nickname)
r1=Riven('銳雯雯',57,200,'比基尼')
r1.fly()
print(r1.skin)
'''
運(yùn)行結(jié)果
銳雯雯 is flying
比基尼
'''
六、繼承的實現(xiàn)與原理
python到底是如何實現(xiàn)繼承的,對于你定義的每一個類,python會計算出一個方法解析順序(MRO)列表,這個MRO列表就是一個簡單的所有基類的線性順序列表,例如
>>> F.mro() #等同于F.__mro__
[
,
,
,
,
,
,
]
為了實現(xiàn)繼承,python會在MRO列表上從左到右開始查找基類,直到找到第一個匹配這個屬性的類為止。而這個MRO列表的構(gòu)造是通過一個C3線性化算法來實現(xiàn)的。我們不去深究這個算法的數(shù)學(xué)原理,它實際上就是合并所有父類的MRO列表并遵循如下三條準(zhǔn)則:
1、子類會先于父類被檢查。
2、多個父類會根據(jù)它們在列表中的順序被檢查。
3、如果對下一個類存在兩個合法的選擇,選擇第一個父類。
在Java和C#中子類只能繼承一個父類,而Python中子類可以同時繼承多個父類,如果繼承了多個父類,那么屬性的查找方式有兩種,分別是:深度優(yōu)先和廣度優(yōu)先。
示范代碼:
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__) #只有新式才有這個屬性可以查看線性列表,經(jīng)典類沒有這個屬性
#新式類繼承順序:F->D->B->E->C->A
#經(jīng)典類繼承順序:F->D->B->A->E->C
#python3中統(tǒng)一都是新式類
#pyhon2中才分新式類與經(jīng)典類
七、在子類中調(diào)用父類的方法
在子類派生出的新方法中,往往需要重用父類的方法,我們有兩種方式實現(xiàn)
方式一:指名道姓,即父類名.父類方法()
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) # 指名道姓的調(diào)用
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) 就相當(dāng)于實例本身 在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()
這兩種方式的區(qū)別是:方式一是跟繼承沒有關(guān)系的,而方式二的super()是依賴于繼承的, 并且即使沒有直接繼承關(guān)系,super仍然會按照mro繼續(xù)往后查找。
#A沒有繼承B,但是A內(nèi)super會基于C.mro()繼續(xù)往后找
class A:
def test(self):
super().test()
class B:
def test(self):
print('from B')
class C(A,B):
pass
c=C()
c.test() #打印結(jié)果:from B
print(C.mro())
#[
,
,
,
]
更多文章、技術(shù)交流、商務(wù)合作、聯(lián)系博主
微信掃碼或搜索:z360901061
微信掃一掃加我為好友
QQ號聯(lián)系: 360901061
您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點擊下面給點支持吧,站長非常感激您!手機(jī)微信長按不能支付解決辦法:請將微信支付二維碼保存到相冊,切換到微信,然后點擊微信右上角掃一掃功能,選擇支付二維碼完成支付。
【本文對您有幫助就好】元

