您好,登錄后才能下訂單哦!
這篇文章主要介紹了Python類和對(duì)象如何應(yīng)用的相關(guān)知識(shí),內(nèi)容詳細(xì)易懂,操作簡(jiǎn)單快捷,具有一定借鑒價(jià)值,相信大家閱讀完這篇Python類和對(duì)象如何應(yīng)用文章都會(huì)有所收獲,下面我們一起來(lái)看看吧。
我們前面其實(shí)已經(jīng)接觸過(guò)封裝的概念,把亂七八糟的數(shù)據(jù)扔進(jìn)列表里面,這是一種封裝,是數(shù)據(jù)層面的封裝;把常用的代碼段打包成一個(gè)函數(shù),這也是一種封裝,是語(yǔ)句層面的封裝;現(xiàn)在我們要學(xué)習(xí)的對(duì)象,也是一種封裝的思想, 對(duì)象的來(lái)源是模擬真是世界,將數(shù)據(jù)和代碼都封裝在了一起。
打個(gè)比方,烏龜就是真實(shí)世界的一個(gè)對(duì)象,通常會(huì)從兩個(gè)部分來(lái)描述它。
(1)從靜態(tài)的特征描述:例如,綠色的,有四條腿,有外殼等等,這是靜態(tài)一方面的描述。
(2)從動(dòng)態(tài)的行為描述:例如,它會(huì)爬,如果追它,它還會(huì)跑,有時(shí)還會(huì)咬人,睡覺(jué)等等,這都是從行為方面進(jìn)行描述的。
Python中的對(duì)象也是如此,一個(gè)對(duì)象的特征稱為“屬性”,一個(gè)對(duì)象的行為稱為“方法”。:
如果將烏龜寫成代碼,將會(huì)是下面這樣:
class Turtle: # Python中的類名約定以大寫字母開(kāi)頭 # 特征的描述稱為屬性,在代碼層面看來(lái)其實(shí)就是變量 color = 'green' legs = 4 shell = True # 方法實(shí)際就是函數(shù),通過(guò)調(diào)用這些函數(shù)來(lái)完成某些工作 def climb(self): print('向前爬') def run(self): print('向前跑') def bite(self): print('咬人') def sleep(self): print('睡覺(jué)')
以上代碼定義了對(duì)象的特征(屬性)和行為(方法),但還不是一個(gè)完整的對(duì)象,將定義的這些稱為類(Class)。需要使用類來(lái)創(chuàng)建一個(gè)真正的對(duì)象,這個(gè)對(duì)象就叫作這個(gè)類的一個(gè)實(shí)例(Instance),也叫實(shí)例對(duì)象(Instance Objects)。
舉個(gè)例子,這就像工廠需要生產(chǎn)一系列玩具,需要先作出這個(gè)玩具的模具,然后根據(jù)這個(gè)模具再進(jìn)行批量生產(chǎn)。
那么怎么創(chuàng)建真正的實(shí)例對(duì)象呢?創(chuàng)建一個(gè)對(duì)象,也叫類的實(shí)例化,其實(shí)很簡(jiǎn)單:
# 首先要有上面那一段類的定義 tt = Turtle()
注意:類名后面跟著小括號(hào),這跟調(diào)用函數(shù)是一樣的。所以在Python中,類名約定用大寫字母開(kāi)頭,函數(shù)用小寫字母開(kāi)頭,這樣更容易區(qū)分。另外,賦值操作并不是必需的,但如果沒(méi)有把創(chuàng)建好的實(shí)例對(duì)象賦值給一個(gè)變量,這個(gè)對(duì)象就沒(méi)辦法使用,因?yàn)闆](méi)有引用指向這個(gè)實(shí)例,最終會(huì)被Python的垃圾回收機(jī)制自動(dòng)回收。
如果要 調(diào)用對(duì)象里的方法,使用點(diǎn)操作符(.) 即可。
接下來(lái)我們看一段代碼,再深入理解一下類、類對(duì)象和實(shí)例對(duì)象三個(gè)概念:
從這個(gè)例子可以看出,對(duì)實(shí)例對(duì)象c的count屬性進(jìn)行賦值后,就相當(dāng)于覆蓋了類對(duì)象C的count屬性。如下圖所示,如果沒(méi)有賦值覆蓋,那么引用的是類對(duì)象的count屬性。
需要注意的是,類中定義的屬性是靜態(tài)變量,類的屬性是與類對(duì)象進(jìn)行綁定,并不會(huì)依賴任何它的實(shí)例對(duì)象。
另外,如果屬性的名字跟方法名相同,屬性會(huì)覆蓋方法:
為了避免名字上的沖突,應(yīng)該遵守一些約定俗成的規(guī)矩:
(1)不要試圖在一個(gè)類里面定義出所有能想到的特性和方法,應(yīng)該利用繼承和組合機(jī)制進(jìn)行擴(kuò)展。
(2)用不同的詞性命名,如屬性名用名詞、方法名用動(dòng)詞,并使用駝峰命名法等。
細(xì)心的讀者發(fā)現(xiàn)對(duì)象的方法都會(huì)有一個(gè)self參數(shù),那么這個(gè)self是什么呢?如果你接觸過(guò)C++,那么你應(yīng)該很容易對(duì)號(hào)入座,Python的self其實(shí)就相當(dāng)于C++的this指針。
如果你此前沒(méi)有接觸過(guò)任何編程語(yǔ)言,那么簡(jiǎn)單說(shuō),如果把類比作圖紙,那么由類實(shí)例化后的對(duì)象才是真正可以住的房子。根據(jù)一張圖紙可以設(shè)計(jì)出成千上萬(wàn)的房子,它們外觀都差不多,但是每一個(gè)房子都有不同的主人。每個(gè)人要找到自己的房子,那self就相當(dāng)于這里的門牌號(hào),有了self,你就可以輕松找到自己的房子。
Python的self參數(shù)就是同一個(gè)道理,由一個(gè)類可以生成無(wú)數(shù)個(gè)對(duì)象,當(dāng)一個(gè)對(duì)象方法被調(diào)用的時(shí)候,對(duì)象會(huì)將自身的引用作為第一個(gè)參數(shù)傳給該方法,那么Python就知道需要操作哪個(gè)對(duì)象的方法了。
舉個(gè)簡(jiǎn)單的例子:
一般面向?qū)ο蟮木幊陶Z(yǔ)言都會(huì)區(qū)分公有和私有的數(shù)據(jù)類型,像C++和Java它們使用public和private關(guān)鍵字用于聲明數(shù)據(jù)是公有的還是私有的,但在Python中并沒(méi)有類似的關(guān)鍵字來(lái)修飾。
默認(rèn)上對(duì)象的屬性和方法都是公開(kāi)的,可以直接通過(guò)點(diǎn)操作符(.)進(jìn)行訪問(wèn):
為了實(shí)現(xiàn)類似私有變量的特征,Python內(nèi)部采用了一種叫name mangling(名字改編)的技術(shù),在Python中定義私有變量只需要在變量名或函數(shù)名前加上“_ _”兩個(gè)下劃線,那么這個(gè)函數(shù)或變量就會(huì)成為私有的了:
這樣,在外部將變量名“隱藏”起來(lái)了,理論上如果要訪問(wèn),就要從內(nèi)部進(jìn)行:
但是認(rèn)真想一下這個(gè)技術(shù)的名字name mangling(名字改編),那就不難發(fā)現(xiàn)其實(shí)Python只是把雙下橫線開(kāi)頭的變量進(jìn)行了改名而已。實(shí)際上,在外部使用“_類名_ _變量名”即可訪問(wèn)雙下橫線開(kāi)頭的私有變量了:
說(shuō)明:Python目前的私有機(jī)制其實(shí)是偽私有的,Python的類是沒(méi)有權(quán)限控制的,所有的變量都是可以被外部調(diào)用的。
舉個(gè)例子來(lái)說(shuō)明繼承。例如現(xiàn)在有個(gè)游戲,需要對(duì)魚類進(jìn)行細(xì)分,有金魚(Goldfish)、鯉魚(Carp)、三文魚(Salmon)以及鯊魚(Shark)。那么我們能不能不要每次都從頭到尾去重新定義一個(gè)新的魚類呢?因?yàn)槲覀冎来蠖鄶?shù)魚的屬性和方法是相似的,如果有一種機(jī)制可以讓這些相似的東西得以自動(dòng)傳遞,那么就方便多了。這就是繼承。
繼承的語(yǔ)法很簡(jiǎn)單:
c l a s s 類 名 ( 被 繼 承 的 類 ) : . . . class 類名(被繼承的類): \\ \quad ... class類名(被繼承的類):...
被繼承的類稱為基類、父類或超類;繼承者稱為子類,一個(gè)子類可以繼承它的父類的任何屬性和方法。
舉個(gè)例子:
需要注意的是,如果子類中定義與父類同名的方法或?qū)傩?,則會(huì)自動(dòng)覆蓋父類對(duì)應(yīng)的方法或?qū)傩?/strong>:
接下來(lái),嘗試寫一下開(kāi)頭提到的金魚(Goldfish)、鯉魚(Carp)、三文魚(Salmon)以及鯊魚(Shark)的例子。
import random as r class Fish: def __init__(self): self.x = r.randint(0, 10) self.y = r.randint(0, 10) def move(self): # 這里主要演示類的繼承機(jī)制,就不考慮檢查場(chǎng)景邊界和移動(dòng)方向問(wèn)題 # 假設(shè)所有的魚都是一路向西游 self.x -= 1 print("我的位置是:", self.x, self.y) # 金魚 class Goldfish(Fish): pass # 鯉魚 class Carp(Fish): pass #三文魚 class Salmon(Fish): pass # 上面三種魚都是食物,直接繼承Fish類的全部屬性和方法 # 下面定義鯊魚類,除了繼承Fish類的屬性和方法,還要添加一個(gè)吃的方法 class Shark(Fish): def __init__(self): self.hungry = True def eat(self): if self.hungry: print("吃掉你!") self.hungry = False else: print("太飽了,吃不下了~")
首先運(yùn)行這段代碼,然后進(jìn)行測(cè)試:
同樣是繼承于Fish類,為什么金魚(goldfish)可以移動(dòng),而鯊魚(shark)一移動(dòng)就報(bào)錯(cuò)呢?
可以看到報(bào)錯(cuò)提示為:Shark對(duì)象沒(méi)有x屬性,這是因?yàn)樵赟hark類中,重寫了_ _init_ _()方法,但新的_ _init_ _()方法里面沒(méi)有初始化鯊魚的x坐標(biāo)和y坐標(biāo),因此調(diào)用move()方法就會(huì)出錯(cuò)。
那么解決這個(gè)問(wèn)題,只要在鯊魚類中重寫_ _init_ _()方法的時(shí)候先調(diào)用基類Fish的_ _init_ _()方法。
下面介紹兩種可以實(shí)現(xiàn)的技術(shù):
(1)調(diào)用未綁定的父類方法
(2)使用super函數(shù)
什么是調(diào)用未綁定的父類方法?舉個(gè)例子:
修改之后,再運(yùn)行下發(fā)現(xiàn)鯊魚也可以成功移動(dòng)了:
這里需要注意的是,這個(gè)self并不是父類Fish的實(shí)例對(duì)象,而是子類Shark的實(shí)例對(duì)象。所以這里說(shuō)的未綁定是指并不需要綁定父類的實(shí)例對(duì)象,使用子類的實(shí)例對(duì)象代替即可。
super函數(shù)能夠幫助我們自動(dòng)找到基類的方法,而且還為我們傳入了self參數(shù),這樣就不需要做這些事情了:
運(yùn)行后得到同樣的結(jié)果:
除此之外,Python還支持多重繼承,就是可以同時(shí)繼承多個(gè)父類的屬性和方法:
c l a s s 類 名 ( 父 類 1 , 父 類 2 , 父 類 3 , . . . ) : . . . class 類名(父類1,父類2,父類3,...):\\ \quad ... class類名(父類1,父類2,父類3,...):...
舉個(gè)例子:
這就是基本的多重繼承語(yǔ)法,但多重繼承很容易導(dǎo)致代碼混亂,所以當(dāng)你不確定是否真的必須使用多重繼承的時(shí)候,請(qǐng)盡量避免使用它,因?yàn)橛行r(shí)候會(huì)出現(xiàn)不可預(yù)見(jiàn)的BUG。
前面學(xué)習(xí)了繼承的概念,又提到了多重繼承,但如果現(xiàn)在我們有了烏龜類、魚類,現(xiàn)在要求定義一個(gè)類,叫水池,水池里要有烏龜和魚。用多重繼承就顯得很奇怪,因?yàn)樗睾蜑觚?、魚是不同物種,那怎樣把它們組合成一個(gè)水池的類呢?
其實(shí)在Python中很簡(jiǎn)單,直接把需要的類放進(jìn)去實(shí)例化就可以了,這就叫組合:
先運(yùn)行上段代碼,然后測(cè)試:
Python的對(duì)象有許多神奇的方法,如果你的對(duì)象實(shí)現(xiàn)了這些方法中的某一個(gè),那么這個(gè)方法就會(huì)在特殊情況下被Python所調(diào)用,而這一切都是自動(dòng)發(fā)生的。
通常把_ _init_ _()方法稱為構(gòu)造方法,只要實(shí)例化一個(gè)對(duì)象,這個(gè)方法就會(huì)在對(duì)象被創(chuàng)建時(shí)自動(dòng)調(diào)用。實(shí)例化對(duì)象時(shí)是可以傳入?yún)?shù)的,這些參數(shù)會(huì)自動(dòng)傳入_ _init_ _()方法中,可以通過(guò)重寫這個(gè)方法來(lái)自定義對(duì)象的初始化操作。
舉個(gè)例子:
有些讀者可能會(huì)問(wèn),有些時(shí)候在類定義時(shí)寫_ _init_ _()方法,有時(shí)候卻沒(méi)有,這是為什么呢?看下面這個(gè)例子:
這里需要注意的是,_ _init_ _()方法的返回值一定是None,不能是其他:
所以,一般在需要進(jìn)行初始化的時(shí)候才重寫_ _init_ _()方法。所以這個(gè)_ _init_ _()方法并不是實(shí)例化對(duì)象時(shí)第一個(gè)被調(diào)用的方法。
_ _new_ _()方法才是一個(gè)對(duì)象實(shí)例化的時(shí)候所調(diào)用的第一個(gè)方法。與其他方法不同的是,它的第一個(gè)參數(shù)不是self而是這個(gè)類(cls),而其他的參數(shù)會(huì)直接傳遞給_ _init_ _()方法的。
_ _new_ _()方法需要返回一個(gè)實(shí)例對(duì)象,通常是cls這個(gè)類實(shí)例化的對(duì)象,當(dāng)然你也可以返回其他對(duì)象。
_ _new_ _()方法平時(shí)很少去重寫它,一般讓Python用默認(rèn)的方案執(zhí)行即可。但是有一種情況需要重寫這個(gè)方法,就是當(dāng)繼承一個(gè)不可變的類型的時(shí)候,它的特性就顯得尤為重要了。
如果說(shuō)_ _init_ _()和_ _new_ _()方法是對(duì)象的構(gòu)造器的話,那么Python也提供了一個(gè)析構(gòu)器,叫作_ _del_ _()方法。當(dāng)對(duì)象將要被銷毀的時(shí)候,這個(gè)方法就會(huì)被調(diào)用。但是需要注意的是,并非 del x 就相當(dāng)于自動(dòng)調(diào)用 x._ _del_ _(),_ _del_ _()方法是當(dāng)垃圾回收機(jī)制回收這個(gè)對(duì)象的時(shí)候調(diào)用的。 舉個(gè)例子:
前面提到過(guò)綁定的概念,那到底什么是綁定呢?Python中嚴(yán)格要求了方法需要有實(shí)例才能被調(diào)用,這種限制其實(shí)就是Python所謂的綁定概念。
有人可能會(huì)這么嘗試,而且發(fā)現(xiàn)也可以調(diào)用:
但是,這樣做會(huì)有一個(gè)問(wèn)題,就是根據(jù)類實(shí)例化后的對(duì)象根本無(wú)法調(diào)用里面的函數(shù):
實(shí)際上是由于Python的綁定機(jī)制,這里自動(dòng)把bb對(duì)象作為第一個(gè)參數(shù)傳入,所以才會(huì)出現(xiàn)TypeError。
再看一個(gè)例子:
_ _dict_ _屬性是由一個(gè)字典組成,字典中僅有實(shí)例對(duì)象的屬性,不顯示類屬性和特殊屬性,鍵表示的是屬性名,值表示屬性相應(yīng)的數(shù)據(jù)值。
現(xiàn)在實(shí)例對(duì)象dd有了兩個(gè)新屬性,而且這兩個(gè)屬性是僅屬于實(shí)例對(duì)象的:
為什么會(huì)這樣?其實(shí)這完全歸功于self參數(shù):當(dāng)實(shí)例對(duì)象dd去調(diào)用setXY方法的時(shí)候,它傳入的第一個(gè)參數(shù)就是dd,那么self.x = 4, self.y = 5也就相當(dāng)于dd.x = 4, dd.y = 5,所以在實(shí)例對(duì)象,甚至類對(duì)象中都看不到x和y,是因?yàn)?strong>這兩個(gè)屬性是只屬于實(shí)例對(duì)象dd的。
如果把類實(shí)例刪掉,實(shí)例對(duì)象dd還能否調(diào)用printXY方法?答案是可以的:
關(guān)于“Python類和對(duì)象如何應(yīng)用”這篇文章的內(nèi)容就介紹到這里,感謝各位的閱讀!相信大家對(duì)“Python類和對(duì)象如何應(yīng)用”知識(shí)都有一定的了解,大家如果還想學(xué)習(xí)更多知識(shí),歡迎關(guān)注億速云行業(yè)資訊頻道。
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。