【Python基礎(chǔ)】Python 面向?qū)ο缶幊?上篇)

我的施工計(jì)劃圖
已完成專題包括:
4列表專題
今天是面向?qū)ο缶幊痰纳掀夯A(chǔ)專題
Python 面向?qū)ο缶幊?/span>
面向?qū)ο蟪绦蛟O(shè)計(jì)思想,首先思考的不是程序執(zhí)行流程,它的核心是抽象出一個(gè)對(duì)象,然后構(gòu)思此對(duì)象包括的數(shù)據(jù),以及操作數(shù)據(jù)的行為方法。
本專題主要討論面向?qū)ο缶幊?OOP)的基礎(chǔ)和進(jìn)階知識(shí),實(shí)際開(kāi)發(fā)模型中OOP的主要實(shí)踐,盡量使用最貼切的例子。
基礎(chǔ)專題
1 類定義
動(dòng)物是自然界一個(gè)龐大的群體,以建模動(dòng)物類為主要案例論述OOP編程。
Python語(yǔ)言創(chuàng)建動(dòng)物類的基本語(yǔ)法如下,使用class關(guān)鍵字定義一個(gè)動(dòng)物類:
class?Animal():
????pass
類里面可包括數(shù)據(jù),如下所示的Animal類包括兩個(gè)數(shù)據(jù):self.name和self.speed:
class?Animal():
???def?__init__(self,name,speed):
???????self.name?=?name?#?動(dòng)物名字
???????self.speed?=?speed?#?動(dòng)物行走或飛行速度
注意到類里面通過(guò)系統(tǒng)函數(shù)__init__為類的2個(gè)數(shù)據(jù)賦值,數(shù)據(jù)前使用self保留字。
self的作用是指名這兩個(gè)數(shù)據(jù)是實(shí)例上的,而非類上的。
同時(shí)注意到__init__方法的第一個(gè)參數(shù)也帶有self,所以也表明此方法是實(shí)例上的方法。
2 實(shí)例
理解什么是實(shí)例上的數(shù)據(jù)或方法,什么是類上的數(shù)據(jù),需要先建立實(shí)例的概念,類的概念,如下:
#?生成一個(gè)名字叫加菲貓、行走速度8km/h的cat對(duì)象
cat?=?Animal('加菲貓',8)?
cat就是Animal的實(shí)例,也可以一次創(chuàng)建成千上百個(gè)實(shí)例,如下創(chuàng)建1000只蜜蜂:
bees?=?[Animal('bee'+str(i),5)?for?i?in?range(1000)]
總結(jié):自始至終只使用一個(gè)類Animal,但卻可以創(chuàng)建出許多個(gè)它的實(shí)例,因此是一對(duì)多的關(guān)系。
實(shí)例創(chuàng)建完成后,下一步打印它看看:
In?[1]:?print(cat)???????????????????????????????????????????????????????????
<__main__.Animal?object?at?0x7fce3a596ad0>
結(jié)果顯示它是Animal對(duì)象,其實(shí)打印結(jié)果顯示實(shí)例屬性信息會(huì)更友好,那么怎么實(shí)現(xiàn)呢?
3 打印實(shí)例
只需重新定義一個(gè)系統(tǒng)(又稱為魔法)函數(shù)__str__ ,就能讓打印實(shí)例顯示的更加友好:
class?Animal():
???def?__init__(self,name,speed):
???????self.name?=?name?#?動(dòng)物名字
???????self.speed?=?speed?#?動(dòng)物行走或飛行速度
??
???def?__str__(self):
????????return?'''Animal({0.name},{0.speed})?is?printed
????????????????name={0.name}
????????????????speed={0.speed}'''.format(self)
使用0.數(shù)據(jù)名稱的格式,這是類專有的打印格式。
現(xiàn)在再打印:
cat?=?Animal('加菲貓',8)
print(cat)
打印信息如下:
Animal(加菲貓,8)?is?printed
????????????????name=加菲貓
????????????????speed=8
以上就是想要的打印格式,看到實(shí)例的數(shù)據(jù)值都正確。
4 屬性
至此,我們都稱類里的name和speed稱為數(shù)據(jù),其實(shí)它們有一個(gè)專業(yè)名稱:屬性。
同時(shí),上面還有一個(gè)問(wèn)題我們沒(méi)有回答完全,什么是類上的屬性?
如下,在最新Animal類定義基礎(chǔ)上,再添加一個(gè)cprop屬性,它前面沒(méi)有self保留字:
class?Animal():
???cprop?=?"我是類上的屬性cprop"
???
???def?__init__(self,name,speed):
???????self.name?=?name?#?動(dòng)物名字
???????self.speed?=?speed?#?動(dòng)物行走或飛行速度
??
???def?__str__(self):
????????return?'''Animal({0.name},{0.speed})?is?printed
????????????????name={0.name}
????????????????speed={0.speed}'''.format(self)
類上的屬性直接使用類便可引用:
In?[1]:?Animal.cprop???????????????????????????????????????????????????????????
Out[1]:?'我是類上的屬性cprop'
類上的屬性,實(shí)例同樣可以引用,并且所有的實(shí)例都共用此屬性值:
In?[1]:?cat?=?Animal('加菲貓',8)
In?[2]:?cat.cprop??????????????????????????????????????????????????????????????
Out[2]:?'我是類上的屬性cprop'
Python作為一門動(dòng)態(tài)語(yǔ)言,支持屬性的動(dòng)態(tài)添加和刪除。
如下cat實(shí)例原來(lái)不存在color屬性,但是賦值時(shí)不光不會(huì)報(bào)錯(cuò),相反會(huì)直接將屬性添加到cat上:
cat.color?=?'grap'
那么,如何驗(yàn)證cat是否有color屬性呢?使用內(nèi)置函數(shù)hasattr:
In?[24]:?hasattr(cat,'color')?#?cat?已經(jīng)有`color`屬性??????????????????????????
Out[24]:?True
但是注意:以上添加屬性方法僅僅為cat實(shí)例本身添加,而不會(huì)為其他實(shí)例添加:
In?[26]:?monkey?=?Animal('大猩猩',2)????????????????????????????????????????????
In?[27]:?hasattr(monkey,'color')?????????????????????????????????????????????
Out[27]:?False
monkey實(shí)例并沒(méi)有color屬性,注意與__init__創(chuàng)建屬性方法的區(qū)別。
5 private,protected,public
像name和speed屬性,引用此實(shí)例的對(duì)象都能訪問(wèn)到它們,如下:
#?模塊名稱:manager.py
import?time
class?Manager():
????def?__init__(self,animal):
????????self.animal?=?animal
????????
????def?recordTime(self):
????????self.__t?=?time.time()
????????print('feeding?time?for?%s(行走速度為:%s)?is?%.0f'%(self.animal.name,self.animal.speed,self.__t))
????
????def?getFeedingTime(self):
????????return?'%0.f'%(self.__t,)?
使用以上Manager類,創(chuàng)建一個(gè)cat實(shí)例,xiaoming實(shí)例引用cat:
cat?=?Animal('加菲貓',8)
xiaoming?=??Manager(cat)?
xiaoming的recordTime方法引用里,引用了animal的兩個(gè)屬性name和speed:
In[1]:?xiaoming.recordTime()
Out[1]:?feeding?time?for?加菲貓(行走速度為:8)?is?1595681304
注意看到self.__t屬性,它就是一個(gè)私有屬性,只能被Manager類內(nèi)的所有方法引用,如被方法getFeedingTime方法引用。但是,不能被其他類引用。
如果我們連speed這個(gè)屬性也不想被其他類訪問(wèn),那么只需將self.speed修改為self.__speed:
同時(shí)Manager類的self.animal.speed修改為self.animal.__speed,再次調(diào)用下面方法時(shí):
xiaoming.recordTime()
就會(huì)報(bào)沒(méi)有__speed屬性的異常,從而驗(yàn)證了__speed屬性已經(jīng)變?yōu)轭悆?nèi)私有,不會(huì)暴露在外面。

總結(jié):name屬性相當(dāng)于java的public屬性,而__speed相當(dāng)于java的private屬性。
下面在說(shuō)繼承時(shí),講解protected屬性,實(shí)際上它就是帶有1個(gè)_的屬性,它只能被繼承的類所引用。
6 繼承
上面已經(jīng)講完了OOP三大特性中的封裝性,而繼承是它的第二大特性。子類繼承父類的所有public和protected數(shù)據(jù)和方法,極大提高了代碼的重用性。
如上創(chuàng)建的Animal類最新版本為:
class?Animal():
???cprop?=?"我是類上的屬性cprop"
???
???def?__init__(self,name,speed):
???????self.name?=?name?#?動(dòng)物名字
???????self.__speed?=?speed?#?動(dòng)物行走或飛行速度
??
???def?__str__(self):
????????return?'''Animal({0.name},{0.__speed})?is?printed
????????????????name={0.name}
????????????????speed={0.__speed}'''.format(self)
現(xiàn)在有個(gè)新的需求,要重新定義一個(gè)Cat貓類,它也有name和speed兩個(gè)屬性,同時(shí)還有color和genre兩個(gè)屬性,打印時(shí)只需要打印name和speed兩個(gè)屬性就行。
因此,基本可以復(fù)用基類Animal,但需要修改__speed屬性為受保護(hù)(protected)的_speed屬性,這樣子類都可以使用此屬性,而外部還是訪問(wèn)不到它。
綜合以上,Cat類的定義如下:
class?Cat(Animal):
????def?__init__(self,name,speed,color,genre):
????????super().__init__(name,speed)
????????self.color?=?color?
????????self.genre?=?genre
首先使用super()方法找到Cat的基類Animal,然后引用基類的__init__方法,這樣復(fù)用基類的方法。
使用Cat類,打印時(shí),又復(fù)用了基類的 __str__方法:
jiafeimao?=?Cat('加菲貓',8,'gray','CatGenre')
print(jiafeimao)
打印結(jié)果:
Animal(加菲貓,8)?is?printed
????????????????name=加菲貓
????????????????speed=8
以上就是基本的繼承使用案例,繼承要求基類定義的數(shù)據(jù)和行為盡量標(biāo)準(zhǔn)、盡量精簡(jiǎn),以此提高代碼復(fù)用性。
7 多態(tài)
如果說(shuō)OOP的封裝和繼承使用起來(lái)更加直觀易用,那么作為第三大特性的多態(tài),在實(shí)踐中真正運(yùn)用起來(lái)就不那么容易。有的讀者OOP編程初期,可能對(duì)多態(tài)的價(jià)值體會(huì)不深刻,甚至都已經(jīng)淡忘它的存在。
那么問(wèn)題就在:多態(tài)到底真的有用嗎?到底使用在哪些場(chǎng)景?
多態(tài)價(jià)值很大,使用場(chǎng)景很多,幾乎所有的系統(tǒng)或軟件,都能看到它的應(yīng)用。這篇文章盡可能通過(guò)一個(gè)精簡(jiǎn)的例子說(shuō)明它的價(jià)值和使用方法。如果不用多態(tài),方法怎么寫;使用多態(tài),又是怎么寫。
為了一脈相承,做到一致性,仍然基于上面的案例,已經(jīng)創(chuàng)建好的Cat類要有一個(gè)方法打印和返回它的爬行速度。同時(shí)需要再創(chuàng)建一個(gè)類Bird,要有一個(gè)方法打印和返回它的飛行速度;
如果不使用多態(tài),為Cat類新增一個(gè)方法:
class?Cat(Animal):
????def?__init__(self,name,speed,color,genre):
????????super().__init__(name,speed)
????????self.color?=?color?
????????self.genre?=?genre
????#?添加方法
????def?getRunningSpeed(self):
????????print('running?speed?of?%s?is?%s'?%(self.name,?self._speed))
????????return?self._speed
重新創(chuàng)建一個(gè)Bird類:
class?Bird(Animal):
????def?__init__(self,name,speed,color,genre):
????????super().__init__(name,speed)
????????self.color?=?color?
????????self.genre?=?genre
????#?添加方法
????def?getFlyingSpeed(self):
????????print('flying?speed?of?%s?is?%s'?%(self.name,?self._speed))
????????return?self._speed
最后,上面創(chuàng)建的Manager類會(huì)引用Cat和Bird類,但是需要修改recordTime方法,因?yàn)镃at它是爬行的,Bird它是飛行的,所以要根據(jù)對(duì)象類型的不同做邏輯區(qū)分,如下所示:
#?模塊名稱:manager.py
import?time
from?animal?import?(Animal,Cat,Bird)
class?Manager():
????def?__init__(self,animal):
????????self.animal?=?animal
????????
????def?recordTime(self):
????????self.__t?=?time.time()
????????if?isinstance(self.animal,?Cat):
????????????print('feeding?time?for?%s?is?%.0f'%(self.animal.name,self.__t))
????????????self.animal.getRunningSpeed()
????????if?isinstance(self.animal,Bird):
????????????print('feeding?time?for?%s?is?%.0f'%(self.animal.name,self.__t))
????????????self.animal.getFlyingSpeed()
????def?getFeedingTime(self):
????????return?'%0.f'%(self.__t,)?
如果再來(lái)一個(gè)類,我們又得需要修改recordTime,再增加一個(gè)if分支,從軟件設(shè)計(jì)角度講,這種不斷破壞封裝的行為不可取。
但是,使用多態(tài),就可以保證recordTime不被修改,不必寫很多if分支。怎么來(lái)實(shí)現(xiàn)呢?
首先,在基類Animal中創(chuàng)建一個(gè)基類方法,然后Cat和Bird分別重寫此方法,最后傳入到Manager類的animal參數(shù)是什么類型,在recordTime方法中就會(huì)對(duì)應(yīng)調(diào)用這個(gè)animal實(shí)例的方法,這就是多態(tài)。
代碼如下:
animal2.py 模塊如下:
#?animal2.py?模塊
class?Animal():
???cprop?=?"我是類上的屬性cprop"
???
???def?__init__(self,name,speed):
???????self.name?=?name?#?動(dòng)物名字
???????self._speed?=?speed?#?動(dòng)物行走或飛行速度
??
???def?__str__(self):
????????return?'''Animal({0.name},{0._speed})?is?printed
????????????????name={0.name}
????????????????speed={0._speed}'''.format(self)
???def?getSpeedBehavior(self):
???????pass?
class?Cat(Animal):
????def?__init__(self,name,speed,color,genre):
????????super().__init__(name,speed)
????????self.color?=?color?
????????self.genre?=?genre
????????
????#?重寫方法
????def?getSpeedBehavior(self):
????????print('running?speed?of?%s?is?%s'?%(self.name,?self._speed))
????????return?self._speed
????????
class?Bird(Animal):
????def?__init__(self,name,speed,color,genre):
????????super().__init__(name,speed)
????????self.color?=?color?
????????self.genre?=?genre
????#?重寫方法
????def?getSpeedBehavior(self):
????????print('flying?speed?of?%s?is?%s'?%(self.name,?self._speed))
????????return?self._speed
manager2.py 模塊如下:
#?manager2.py?模塊
import?time
from?animal2?import?(Animal,Cat,Bird)
class?Manager():
????def?__init__(self,animal):
????????self.animal?=?animal
????????
????def?recordTime(self):
????????self.__t?=?time.time()
????????print('feeding?time?for?%s?is?%.0f'%(self.animal.name,self.__t))
????????self.animal.getSpeedBehavior()
????def?getFeedingTime(self):
????????return?'%0.f'%(self.__t,)??
recordTime方法非常清爽,不需要任何if邏輯,只需要調(diào)用我們定義的Animal類的基方法getSpeedBehavior即可。
在使用上面所有類時(shí),Manager(jiafeimao)傳入Cat類實(shí)例時(shí),recordTime方法調(diào)用就被自動(dòng)指向Cat實(shí)例的getSpeedBehavior方法;
Manager(haiying)傳入Bird類實(shí)例時(shí),自動(dòng)指向Bird實(shí)例的getSpeedBehavior方法,這就是多態(tài)和它的價(jià)值,Manager類的方法不必每次都修改,保證了類的封裝性。
if?__name__?==?"__main__":
????jiafeimao?=?Cat('jiafeimao',2,'gray','CatGenre')
????haiying?=?Bird('haiying',40,'blue','BirdGenre')
????Manager(jiafeimao).recordTime()
????print('#'*30)
????Manager(haiying).recordTime()??
總結(jié)
以上就是面向?qū)ο缶幊虒n}的基礎(chǔ)部分,大綱如下:
Python 面向?qū)ο缶幊?/p>
基礎(chǔ)專題
1 類定義
2 實(shí)例
3 打印實(shí)例
4 屬性
5 private,protected,public
6 繼承
7 多態(tài)
總結(jié)
往期精彩回顧
獲取一折本站知識(shí)星球優(yōu)惠券,復(fù)制鏈接直接打開(kāi):
https://t.zsxq.com/662nyZF
本站qq群1003271085。
加入微信群請(qǐng)掃碼進(jìn)群(如果是博士或者準(zhǔn)備讀博士請(qǐng)說(shuō)明):
