目錄:http://www.reibang.com/p/863c446364a8
類的繼承
談類的繼承之前我們要知道:
面向?qū)ο蟮娜筇匦裕悍庋b来候、繼承、多態(tài)
接下來我們就來學習類的繼承唱遭。
一舞吭、什么是類的繼承产喉?
繼承:顧名思義子承父業(yè),合法繼承家產(chǎn)贝椿,就是如果你是獨生子想括,而且你也很孝順,不出意外烙博,你會繼承你父母所有家產(chǎn)瑟蜈,他們的所有財產(chǎn)都會由你使用。
類的繼承:專業(yè)角度來說B繼承A類 B叫做A的子類又稱派生類渣窜,A叫做B的父類铺根,又稱基類或超類。B類以及B的對象使用A類的所有的屬性以及方法乔宿。
那么我們來舉一個最簡單的例子位迂。如果我們想要寫三個類,通常我們的寫法是這樣的。
class Person:
? ? def __init__(self,name,sex,age):
? ? ? ? self.name = name
? ? ? ? self.age = age
? ? ? ? self.sex = sex
class Cat:
? ? def __init__(self,name,sex,age):
? ? ? ? self.name = name
? ? ? ? self.age = age
? ? ? ? self.sex = sex
class Dog:
? ? def __init__(self,name,sex,age):
? ? ? ? self.name = name
? ? ? ? self.age = age
? ? ? ? self.sex = sex
通過上邊的例子可以看出我們定義了三個類掂林,且方法以及方法的屬性都相同臣缀。那么我們用繼承實現(xiàn)同樣的效果。
class Animal:
? ? def __init__(self,name,age,sex):
? ? ? ? self.name=name
? ? ? ? self.age=age
? ? ? ? self.sex=sex
class Person(Animal):? ##括號里為繼承也是父類
? ? pass
class Dog(Animal):
? ? pass
class Cat(Animal):
? ? pass
通過上邊的繼承我們可以看出繼承的優(yōu)點:
1泻帮、節(jié)省代碼
2精置、增強了耦合性
3、使代碼更加規(guī)范化
二锣杂、繼承的分類
就向上面的例子:
Aminal 叫做父類,基類,超類脂倦。
Person Cat Dog: 子類,派生類蹲堂。
繼承:可以分單繼承狼讨,多繼承
三、單繼承
1柒竞、子類以及對象可以調(diào)用父類的屬性及方法政供。
class Animal:
? ? live="有生命的"
? ? def __init__(self,name,age,sex):
? ? ? ? self.name=name
? ? ? ? self.age=age
? ? ? ? self.sex=sex
? ? def eat(self):
? ? ? ? print("動物都需要進食")
class Person(Animal):##括號里為繼承,父類
????pass
## 1朽基、從類名執(zhí)行父類的屬性
print(Person.live)
Person.eat(55)
運行結(jié)果:
有生命的
人類都需要進食
## 2布隔、從對象執(zhí)行父類一切
(1) 實例化對象時一定會執(zhí)行三件事,一定會執(zhí)行__init__
p1=Person("LiMing",20,"男")
print(p1.__dict__)
運行結(jié)果:
{'name': 'LiMing', 'age': 20, 'sex': '男'}
(2)對象執(zhí)行類的父類的屬性稼虎,方法衅檀。
p1=Person("LiMing",20,"男")
print(p1.live)
p1.eat()
運行結(jié)果:
有生命的
動物都需要進食
(3)對象空間重寫繼承父類的方法
p1.eat="LiMing"
print(p1.eat)
運行結(jié)果:
LiMing
#注:這里修改的是p1對象空間,不是父類的方法霎俩。
子類以及子類的對象只能調(diào)用父類的屬性以及方法哀军,不能操作(增刪改)
2、對象執(zhí)行順序
class Animal:
? ? live="有生命的"
? ? def __init__(self,name,age,sex):
? ? ? ? self.name=name
? ? ? ? self.age=age
? ? ? ? self.sex=sex
? ? def eat(self):
? ? ? ? print("動物都需要進食")
class Person(Animal):##括號里為繼承打却,父類
????def eat(self):
? ? ? ? print("人類都需要進食")
p1=Person("LiMing",20,"男")
p1.eat()
運行結(jié)果:
人類都需要進食
總結(jié):對象查找順序:從對象空間找名字杉适,子類找名字,父類找名字
3柳击、?如何既要執(zhí)行父類方法又要執(zhí)行子類方法
方法一:如果想執(zhí)行父類的方法猿推,這個方法與子類方法一起用,那么就在子類的方法中寫上:
父類.__init__(對象,其他參數(shù))
比如:
class Animal:
? ? live="有生命的"
? ? def __init__(self,name,age,sex):
? ? ? ? self.name=name
? ? ? ? self.age=age
? ? ? ? self.sex=sex
? ? def eat(self):
? ? ? ? print("動物都需要進食")
class Person(Animal):##括號里為繼承捌肴,父類
? ? def __init__(self,name,age,sex,hobby):
? ? ? ? Animal.__init__(self,name,age,sex)
? ? ? ? self.hobby=hobby
? ? def eat(self):
? ? ? ? print("人類都需要進食")
p1=Person("LiMing",20,"男","看書")
print(p1.__dict__)
運行結(jié)果:
{'name': 'LiMing', 'age': 20, 'sex': '男', 'hobby': '看書'}
方法二:利用super(子類類名蹬叭,self(這里的參數(shù)可以不寫)).__init__(參數(shù))
比如:
class Animal:
? ? live="有生命的"
? ? def __init__(self,name,age,sex):
? ? ? ? self.name=name
? ? ? ? self.age=age
? ? ? ? self.sex=sex
? ? def eat(self):
? ? ? ? print("動物都需要進食")
class Person(Animal):##括號里為繼承,父類
? ? def __init__(self,name,age,sex,hobby):
? ? ? ? super(Person,self).__init__(name,age,sex)
? ? ? ? self.hobby=hobby
? ? def eat(self):
? ? ? ? print("人類都需要進食")
p1=Person("LiMing",20,"男","看書")
print(p1.__dict__)
運行結(jié)果:
{'name': 'LiMing', 'age': 20, 'sex': '男', 'hobby': '看書'}
舉一反三状知,我們執(zhí)行子類與父類的eat方法
class Animal:
? ? live="有生命的"
? ? def __init__(self,name,age,sex):
? ? ? ? self.name=name
? ? ? ? self.age=age
? ? ? ? self.sex=sex
? ? def eat(self):
? ? ? ? print("動物都需要進食")
class Person(Animal):##括號里為繼承秽五,父類
? ? def __init__(self,hobby):
? ? ? ? self.hobby=hobby
? ? def eat(self):
? ? ? ? print("人類都需要進食")
? ? ? ? super().eat()
p1=Person("看書")
p1.eat()
運行結(jié)果:
人類都需要進食
動物都需要進食
接著我們做幾道單繼承練習題。
1试幽、
class Base:
? ? def __init__(self, num):
? ? ? ? self.num = num
? ? def func1(self):
? ? ? ? print(self.num)
class Foo(Base):
? ? pass
obj = Foo(123)? ? #實例化對象筝蚕,創(chuàng)建對象空間obj,自動執(zhí)行__init__方法卦碾,將123傳給self
obj.func1()? ? ? ? # 123 對象的調(diào)用,執(zhí)行Base中的func1
運行結(jié)果
123
2起宽、
class Base:
? ? def __init__(self, num):
? ? ? ? self.num = num
? ? def func1(self):
? ? ? ? print(self.num)
class Foo(Base):
? ? def func1(self):
? ? ? ? print("Foo. func1", self.num)
obj = Foo(123)
obj.func1()? ? ? ? #根據(jù)對象執(zhí)行順序洲胖,先找---->對象空間------>子類------>父類,所以對象調(diào)用func1坯沪,? ? 子類與父類都有func1時先找子類
運行結(jié)果:
Foo. func1 123
3
class Base:
? ? def __init__(self, num):
? ? ? ? self.num = num
? ? def func1(self):
? ? ? ? print(self.num)
? ? ? ? self.func2()? ? #self----->obj #對象查詢空間绿映,此時的func2應該是子類中的func2,符合執(zhí)行順序
? ? def func2(self):
? ? ? ? print("Base.func2")
class Foo(Base):
? ? def func2(self):
? ? ? ? print("Foo.func2")
obj = Foo(123)
obj.func1()? ? ? ? ? ?# func1是Base中的 func2是?類中的
運行結(jié)果:
123
Foo.func2
4
class Base:
? ? def __init__(self, num):
? ? ? ? self.num = num
? ? def func1(self):
? ? ? ? print(self.num)
? ? ? ? self.func2()
? ? def func2(self):
? ? ? ? print(111, self.num)
class Foo(Base):
? ? def func2(self):
? ? ? ? print(222, self.num)
lst = [Base(1), Base(2), Foo(3)]
for obj in lst:
obj.func1()? ? #調(diào)用父類時只執(zhí)行父類,調(diào)用子類時先執(zhí)行子類腐晾,在執(zhí)行父類
運行結(jié)果:
1
111 1
2
111 2
3
222 3
通過上邊的執(zhí)行過程叉弦,結(jié)果你都答對了嗎?
四、多繼承
談到多繼承我們先來一個案例進行了解吧藻糖!
class God:
? ? def __init__(self,name):
? ? ? ? self.name=name
? ? def fly(self):
? ? ? ? print("會飛")
? ? def climb(self):
? ? ? ? print("神仙累了也需要爬樹")
class Monkey:
? ? def __init__(self,sex):
? ? ? ?self.sex=sex
? ? def climb(self):
? ? ? ? print("爬樹")
class MonkeySun(God,Monkey):
? ? pass
sun=MonkeySun()
sun.climb()
運行結(jié)果:
神仙累了也需要爬樹
#上邊MonkeySun類繼承了God類以及Monkey類淹冰,MonkeySun自然就可以執(zhí)行這兩類中的方法,這就是簡單的多繼承巨柒。多繼承?起來簡單. 也很好理解. 但是多繼承中, 存在著這樣?個問題. 當兩個?類中出現(xiàn)了重名?法的時候. 這時該怎么辦呢? 這時就涉及到如何查找?類?法的這么?個問題.即MRO(method resolution order) 問題. 在python中這是?個很復雜的問題. 因為在不同的python版本中使?的是不同的算法來完成MRO的.
Python中類的種類
在python2x版本中存在兩種類.:
?個叫經(jīng)典類. 在python2.2之前. ?直使?的是經(jīng)典類. 經(jīng)典類在基類的根如果什么都不寫.
?個叫新式類. 在python2.2之后出現(xiàn)了新式類. 新式類的特點是基類的根是object類樱拴。
python3x版本中只有一種類:
python3中使?的都是新式類. 如果基類誰都不繼承. 那這個類會默認繼承 object。
也就是在大體上Python的類分為兩種:一種是基類的根不是object的經(jīng)典類洋满,一種是基類的根是object的新式類晶乔。
我們先說經(jīng)典類的MRO算法。
首先我們舉一個實例:
class A:
? ? pass
class B(A):
? ? pass
class C(A):
? ? pass
class D(B, C):
? ? pass
class E:
? ? pass
class F(D, E):
? ? pass
class G(F, D):
? ? pass
class H:
? ? pass
class Foo(H, G):
? ? pass
然后第一步我們先畫出他的MRO圖牺勾,來展示一下他各個類之間的關(guān)系:
我們乍一看正罢,這個圖就像一棵樹一樣,而經(jīng)典類的MRO算法恰巧也是根據(jù)樹狀結(jié)構(gòu)推導的驻民。
經(jīng)典類的MRO算法就是用來遍歷樹狀結(jié)構(gòu)的深度優(yōu)先遍歷翻具,他的原理如下。
首先我們會從左向右依次查找節(jié)點回还,如果后面查找的節(jié)點在前面已經(jīng)查找過呛占,則忽略不計。
現(xiàn)在我們有一個樹狀結(jié)構(gòu)如上懦趋,那么他的遍歷順序就是:
現(xiàn)在讓我們回到我們實例的MRO圖,然后按照深度優(yōu)先遍歷規(guī)則疹味,他的順序應該是如下:
實例類的MRO:Foo-> H -> G -> F -> E -> D -> B -> A -> C
到這里仅叫,就有了一個問題,假如類A與類C中都有一個方法sum()糙捺,但是類C是繼承類A的诫咱,也就是說類C重寫了類A的方法。按照繼承層級洪灯,如果我們的FOO類使用sum()這個方法坎缭,應該使用的類C中重寫的sum()方法,而不是使用類A中原生的sum()方法。
這也就是Python2.2之前的一個詬病掏呼,Python3徹底改變了這種MRO算法坏快,使用了一種新的方法。
接著我們說新式類的MRO算法憎夷。
Python3使用的這種新的方法叫做MRO序列莽鸿。
這個序列在類被創(chuàng)建之時就計算出來了,他是創(chuàng)建類時的伴生物拾给。他的通用公式如下:
mro(Child(Base1祥得,Base2)) = [ Child ] +merge( mro(Base1), mro(Base2), [ Base1, Base2] )
(其中Child繼承自Base1, Base2)
只看通用公式太過枯燥,讓我們舉個實例看看:假設有類C蒋得,他繼承類A和類B级及。即 class C(A,B)
此時類C的序列是:
mro( C ) = mro( C(A,B) )
? ? ? ? ? ? ? = [C] + merge( mro(A) + mro[B] + [A,B] )
? ? ? ? ? ? ? = [C] + merge( [A] + [B] + [A,B] )
? ? ? ? ? ? ? = [C,A,B]
誒,這里就有一個問題了额衙,為什么是[C,A,B],而不是[C,B,A]呢饮焦,這就要說MRO序列的一個特性--表頭提取。
首先我們先了解兩個概念入偷,表頭追驴,表尾。
我們拿我們的MRO序列[C,A,B]來舉例疏之,C就是表頭殿雪,A,B就是表尾。
接著來說表頭提取锋爪。
我們在進行到[C] + merge( [A] + [B] + [A,B] ) 這一步時丙曙,加號右邊的計算規(guī)則是:
[A] + [B] + [A,B]
我們把這三個序列標位①,②其骄,③亏镰。
1.首先查看①的表頭A,然后去其他兩個序列中拯爽,查看A是否在表尾中索抓,如果在表尾中,則跳過①毯炮,去②中重復操作逼肯。
2.但是①的表頭A不在其他序列的表尾中,所以提出A桃煎,同時刪除其他序列中的A篮幢。
3.這是式子就變成了?[B],提取出來的序列是[A].
4.接著重復第一步操作为迈,直至式子清空三椿。這個式子的結(jié)果就是序列[A,B]
然后我們的式子就變成了[C] +?[A,B]缺菌,序列之間的加法就是連接操作,他們相加的結(jié)果就是:[C,A,B].
最后我們來舉一個實例:
class A:
? ? pass
class B(A):
? ? pass
class C(A):
? ? pass
class D(B, C):
? ? pass
class E:
? ? pass
class F(D, E):
? ? pass
class G(F, D):
? ? pass
class H:
? ? pass
class Foo(H, G):
? ? pass
實例代碼如上搜锰,MRO圖不再贅述伴郁。
根據(jù)上面的MRO序列算法得:
mro( FOO ) = mro( FOO(H,G) )
? ? ? ? ? ? ? ? ? ?= [FOO] + merge( mro(H) + mro[G] + [H,G] )
? ? ? ? ? ? ? ? ? ?= [FOO] + merge( [H] + MRO[G] + [H,G] )
? ? ? ? ? ? ? ? ? ?= [FOO] + merge( [H] + ?[G,F,D,B,C,A,E] + [H,G] )
? ? ? ? ? ? ? ? ? ?= [FOO,H] + merge([G,F,D,B,C,A,E]?+ [G] )
? ? ? ? ? ? ? ? ? ?= [FOO,H,G] + merge([F,D,B,C,A,E])
? ? ? ? ? ? ? ? ? ?=?[FOO,H,G,F,D,B,C,A,E]
此時,因為類G也是一個多重繼承類纽乱,所以要提出來單獨計算蛾绎。
mro( G ) = mro( G(F,D) )
? ? ? ? ? ? ? = [G] + merge( mro(F) + mro[D] + [F,D] )
? ? ? ? ? ? ? = [G] + merge(?[F,D,B,C,A,E] + [D,B,C,A] + [F,D] )
? ? ? ? ? ? ? = [G,F] + merge(?[D,B,C,A,E]?+?[D,B,C,A]?+ [D] )
? ? ? ? ? ? ? = [G,F,D] + merge(?[B,C,A,E]?+?[B,C,A]? )
? ? ? ? ? ? ? = [G,F,D,B,C,A,E]
同理,類F和類D也要進行單獨計算鸦列。
mro( F ) = mro( F(D,E) )
? ? ? ? ? ? ? = [F] + merge( mro(D) + mro[E] + [D,E] )
? ? ? ? ? ? ? = [F] + merge( [D,B,C,A] + [E] + [D,E] )
? ? ? ? ? ? ? = [F,D] + merge( [B,C,A] + [E] + [E] )
? ? ? ? ? ? ? = [F,D,B,C,A,E]
mro( D ) = mro( D(B,C) )
? ? ? ? ? ? ? = [D] + merge( mro(B) + mro[C] + [B,C] )
? ? ? ? ? ? ? = [D] + merge( [B,A] + [C,A] + [B,C] )
? ? ? ? ? ? ? = [D,B] +?merge( [A] + [C,A] + [C] )
? ? ? ? ? ? ? = [D,B,C] +?merge( [A] + [A] )
? ? ? ? ? ? ? = [D,B,C,A]
最后租冠,他的MRO順序為:[FOO,H,G,F,D,B,C,A,E]