目錄:http://www.itdecent.cn/p/863c446364a8
類的繼承
談類的繼承之前我們要知道:
面向?qū)ο蟮娜筇匦裕悍庋b、繼承、多態(tài)
接下來我們就來學(xué)習(xí)類的繼承。
一、什么是類的繼承?
繼承:顧名思義子承父業(yè),合法繼承家產(chǎn),就是如果你是獨(dú)生子,而且你也很孝順,不出意外,你會繼承你父母所有家產(chǎn),他們的所有財(cái)產(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
通過上邊的例子可以看出我們定義了三個類,且方法以及方法的屬性都相同。那么我們用繼承實(shí)現(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)點(diǎn):
1、節(jié)省代碼
2、增強(qiáng)了耦合性
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("動物都需要進(jìn)食")
class Person(Animal):##括號里為繼承,父類
????pass
## 1、從類名執(zhí)行父類的屬性
print(Person.live)
Person.eat(55)
運(yùn)行結(jié)果:
有生命的
人類都需要進(jìn)食
## 2、從對象執(zhí)行父類一切
(1) 實(shí)例化對象時一定會執(zhí)行三件事,一定會執(zhí)行__init__
p1=Person("LiMing",20,"男")
print(p1.__dict__)
運(yùn)行結(jié)果:
{'name': 'LiMing', 'age': 20, 'sex': '男'}
(2)對象執(zhí)行類的父類的屬性,方法。
p1=Person("LiMing",20,"男")
print(p1.live)
p1.eat()
運(yùn)行結(jié)果:
有生命的
動物都需要進(jìn)食
(3)對象空間重寫繼承父類的方法
p1.eat="LiMing"
print(p1.eat)
運(yùn)行結(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("動物都需要進(jìn)食")
class Person(Animal):##括號里為繼承,父類
????def eat(self):
? ? ? ? print("人類都需要進(jìn)食")
p1=Person("LiMing",20,"男")
p1.eat()
運(yùn)行結(jié)果:
人類都需要進(jìn)食
總結(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("動物都需要進(jìn)食")
class Person(Animal):##括號里為繼承,父類
? ? def __init__(self,name,age,sex,hobby):
? ? ? ? Animal.__init__(self,name,age,sex)
? ? ? ? self.hobby=hobby
? ? def eat(self):
? ? ? ? print("人類都需要進(jìn)食")
p1=Person("LiMing",20,"男","看書")
print(p1.__dict__)
運(yùn)行結(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("動物都需要進(jìn)食")
class Person(Animal):##括號里為繼承,父類
? ? def __init__(self,name,age,sex,hobby):
? ? ? ? super(Person,self).__init__(name,age,sex)
? ? ? ? self.hobby=hobby
? ? def eat(self):
? ? ? ? print("人類都需要進(jìn)食")
p1=Person("LiMing",20,"男","看書")
print(p1.__dict__)
運(yùn)行結(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("動物都需要進(jìn)食")
class Person(Animal):##括號里為繼承,父類
? ? def __init__(self,hobby):
? ? ? ? self.hobby=hobby
? ? def eat(self):
? ? ? ? print("人類都需要進(jìn)食")
? ? ? ? super().eat()
p1=Person("看書")
p1.eat()
運(yùn)行結(jié)果:
人類都需要進(jìn)食
動物都需要進(jìn)食
接著我們做幾道單繼承練習(xí)題。
1、
class Base:
? ? def __init__(self, num):
? ? ? ? self.num = num
? ? def func1(self):
? ? ? ? print(self.num)
class Foo(Base):
? ? pass
obj = Foo(123)? ? #實(shí)例化對象,創(chuàng)建對象空間obj,自動執(zhí)行__init__方法,將123傳給self
obj.func1()? ? ? ? # 123 對象的調(diào)用,執(zhí)行Base中的func1
運(yùn)行結(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時先找子類
運(yùn)行結(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應(yīng)該是子類中的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是?類中的
運(yùn)行結(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í)行父類
運(yùn)行結(jié)果:
1
111 1
2
111 2
3
222 3
通過上邊的執(zhí)行過程,結(jié)果你都答對了嗎?
四、多繼承
談到多繼承我們先來一個案例進(jìn)行了解吧!
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()
運(yùn)行結(jié)果:
神仙累了也需要爬樹
#上邊MonkeySun類繼承了God類以及Monkey類,MonkeySun自然就可以執(zhí)行這兩類中的方法,這就是簡單的多繼承。多繼承?起來簡單. 也很好理解. 但是多繼承中, 存在著這樣?個問題. 當(dāng)兩個?類中出現(xiàn)了重名?法的時候. 這時該怎么辦呢? 這時就涉及到如何查找?類?法的這么?個問題.即MRO(method resolution order) 問題. 在python中這是?個很復(fù)雜的問題. 因?yàn)樵诓煌膒ython版本中使?的是不同的算法來完成MRO的.
Python中類的種類
在python2x版本中存在兩種類.:
?個叫經(jīng)典類. 在python2.2之前. ?直使?的是經(jīng)典類. 經(jīng)典類在基類的根如果什么都不寫.
?個叫新式類. 在python2.2之后出現(xiàn)了新式類. 新式類的特點(diǎn)是基類的根是object類。
python3x版本中只有一種類:
python3中使?的都是新式類. 如果基類誰都不繼承. 那這個類會默認(rèn)繼承 object。
也就是在大體上Python的類分為兩種:一種是基類的根不是object的經(jīng)典類,一種是基類的根是object的新式類。
我們先說經(jīng)典類的MRO算法。
首先我們舉一個實(shí)例:
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)推導(dǎo)的。
經(jīng)典類的MRO算法就是用來遍歷樹狀結(jié)構(gòu)的深度優(yōu)先遍歷,他的原理如下。
首先我們會從左向右依次查找節(jié)點(diǎn),如果后面查找的節(jié)點(diǎn)在前面已經(jīng)查找過,則忽略不計(jì)。

現(xiàn)在我們有一個樹狀結(jié)構(gòu)如上,那么他的遍歷順序就是:

現(xiàn)在讓我們回到我們實(shí)例的MRO圖,然后按照深度優(yōu)先遍歷規(guī)則,他的順序應(yīng)該是如下:
實(shí)例類的MRO:Foo-> H -> G -> F -> E -> D -> B -> A -> C
到這里,就有了一個問題,假如類A與類C中都有一個方法sum(),但是類C是繼承類A的,也就是說類C重寫了類A的方法。按照繼承層級,如果我們的FOO類使用sum()這個方法,應(yīng)該使用的類C中重寫的sum()方法,而不是使用類A中原生的sum()方法。
這也就是Python2.2之前的一個詬病,Python3徹底改變了這種MRO算法,使用了一種新的方法。
接著我們說新式類的MRO算法。
Python3使用的這種新的方法叫做MRO序列。
這個序列在類被創(chuàng)建之時就計(jì)算出來了,他是創(chuàng)建類時的伴生物。他的通用公式如下:
mro(Child(Base1,Base2)) = [ Child ] +merge( mro(Base1), mro(Base2), [ Base1, Base2] )
(其中Child繼承自Base1, Base2)
只看通用公式太過枯燥,讓我們舉個實(shí)例看看:假設(shè)有類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就是表尾。
接著來說表頭提取。
我們在進(jìn)行到[C] + merge( [A] + [B] + [A,B] ) 這一步時,加號右邊的計(jì)算規(guī)則是:
[A] + [B] + [A,B]
我們把這三個序列標(biāo)位①,②,③。
1.首先查看①的表頭A,然后去其他兩個序列中,查看A是否在表尾中,如果在表尾中,則跳過①,去②中重復(fù)操作。
2.但是①的表頭A不在其他序列的表尾中,所以提出A,同時刪除其他序列中的A。
3.這是式子就變成了?[B],提取出來的序列是[A].
4.接著重復(fù)第一步操作,直至式子清空。這個式子的結(jié)果就是序列[A,B]
然后我們的式子就變成了[C] +?[A,B],序列之間的加法就是連接操作,他們相加的結(jié)果就是:[C,A,B].
最后我們來舉一個實(shí)例:
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
實(shí)例代碼如上,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]
此時,因?yàn)轭怗也是一個多重繼承類,所以要提出來單獨(dú)計(jì)算。
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也要進(jìn)行單獨(dú)計(jì)算。
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順序?yàn)椋篬FOO,H,G,F,D,B,C,A,E]