Python-類的繼承

目錄: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ī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]

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 寫在前面的話 代碼中的# > 表示的是輸出結(jié)果 輸入 使用input()函數(shù) 用法 注意input函數(shù)輸出的均是字...
    FlyingLittlePG閱讀 3,207評論 0 9
  • 什么是繼承 新類不必從頭編寫 新類從現(xiàn)有的類繼承,就自動擁有了現(xiàn)有類的所有功能 新類只需要編寫現(xiàn)有類缺少的新功能 ...
    Dozing閱讀 1,156評論 0 0
  • 一.繼承介紹 1 什么是繼承 繼承一種新建類的方式,新建的類稱之為子類/派生類,被繼承的類稱之為父類\基類\超...
    aq_wzj閱讀 363評論 0 0
  • property、魔法屬性和魔法方法、多重繼承和多繼承 1.5 property 學(xué)習(xí)目標(biāo) 1. 能夠說出什么要...
    Cestine閱讀 850評論 0 1
  • 一. 子類繼承父類的構(gòu)造函數(shù) 現(xiàn)金盤平臺盤口搭建q<277.03.4.83.6> 子類不重寫 init,實(shí)例化子類...
    789ac4bce924閱讀 446評論 0 0

友情鏈接更多精彩內(nèi)容