2020年10月2日星期五

008 趣學Python程式設計 第1部分 學習程式設計 第8章 如何使用類和物件

 申明本站飛宇網 https://feiyetopro.blogspot.com/自網路收集整理之書籍文章影音僅供預覽交流學習研究,其[書籍、文章、影音]情節內容, 評論屬其個人行為, 與本網站無關。版權歸原作者和出版社所有,請在下載 24 小時內刪除,不得用作商業用途;如果您喜歡其作品,請支持訂閱購買[正版]謝謝!



8章 如何使用類和物件

長頸鹿和人行道有什麼共同點?長頸鹿和人行道都是東西,在漢語裡被稱為名詞,在Python裡則被稱為對象

在電腦的世界裡,物件這個概念很重要。物件是程式組織代碼的方法,它把複雜的想法拆分開來使其更容易被理解。(我們在第4章用海龜作圖時曾經用過一個“Pen”物件。)

要真正理解在Python裡物件是如何工作的,我們先要想想物件的類型。讓我們從長頸鹿和人行道開始。

長頸鹿是一種哺乳動物,哺乳動物是一種動物。長頸鹿同時又是一種活動的物件,因為它是活的。


讓我們再來看看人行道。不用多說,人行路不是活的東西。就讓我們稱它為非活動物件吧(換句話說,它不是活的)。哺乳動物、動物、活動、不動,這些都是給事物分類的方法。

8.1 把事物拆分成類

Python裡,物件是由定義的,我們可以把當成一種把物件分組歸類的方法。圖8-1是長頸鹿和人行道根據我們前面的定義所歸屬的類的樹狀圖。

這裡的最主要的類是Things“東西。在東西類的下面,我們有Inanimate“非活動Animate“活動。它們再進一步分為非活動下的Sidewalks“人行道,以及活動下面的Animals“動物Mammals“哺乳動物Giraffes“長頸鹿

我們可以用類把Python代碼的小片段組織起來。例如,參考一下turtle模組。所有Pythonturtle模組能做的事情(如向前移動、向後移動、向左轉、向右轉)都是Pen這個類裡的函數。可以把一個物件想像成是一個類家族中的一員,我們可以創建任意數量的這個類的物件。我們馬上就會看例子。


8-1 樹狀圖

現在讓我們來創建上面樹狀圖中的那些類吧,從頂部開始。我們用class關鍵字來定義類,後面跟著一個名字。因為Things是最廣泛使用的類,我們要先創建它:


我們把這個類命名為Things並用pass語句來告訴Python我們不會給出更多的資訊了。當我們想提供一個類或者一個函數,卻暫時不想填入具體資訊的時候就可以使用pass

接下來,我們要加入其他的類並在它們之間建立聯繫。

8.1.1 父母與孩子

如果一個類是另一個類家族的一部分,那麼它是另一個類的孩子,另一個類是它的父親。一個類可以同時是另外一些類的孩子和父親。在我們的樹狀圖中,上面的類是父親,下面的是孩子。例如,InanimateAnima都是Things類的孩子,Things是他們的父親。

要告訴Python一個類是另一個類的孩子,就在新類的名字後用括弧加上父親類(以下簡稱父類)的名字,就像這樣:


這樣,我們就創建了一個叫Inanimate的類並通過class Inanimate(Things)來告訴Python它的父類是Things。然後我們創建了叫Animate的類並通過class Animate(Things)告訴Python它的父類也是Things

讓我們用同樣的方法寫出Sidewalks類。我們利用父類Inanimate創建Sidewalks類,就像這樣:


接下來我們也可以同樣地用它們的父類來組織AnimalsMammals,還有Giraffe類:


8.1.2
 增加屬於類的物件

現在我們有了好幾個類,讓我們在這些類裡加入些成員怎麼樣?假設有一個長頸鹿,它的名字叫Reginald。我們知道它屬於Giraffes類,但要用什麼樣的程式術語來描述一隻叫Reginald的長頸鹿呢?我們稱ReginaldGiraffe類的一個物件(物件,object;還可以稱它為實例instance)。我們用下面這段代碼把Reginald“引入Python中:


這段代碼告訴Python創建一個屬於Giraffes類的物件,並把它賦值給變數reginald。像用函數一樣,類的名字後面要用括弧。在這一章的後面部分,我們會學習如何在括弧中使用參數。

但是這個reginald物件能做什麼呢?它到目前為止什麼也不會做。要想讓物件有用,在創建類的時候我們還要定義函數,這樣這個類的物件就可以使用這些函數了。如果不在類的定義之後立刻使用pass關鍵字,我們也可以增加一些函式定義。

8.1.3 定義類中的函數

7章中我們介紹了函數,它是一種重用代碼的方法。我們用和定義其他函數同樣的方式來定義與某個類相關聯的函數,不同的地方只是要在類的定義之下縮進。例如,下面是一個沒有與任何類關聯的普通函數:


下面是兩個屬於類的函數:


8.1.4
 用函數來表示類的特徵

再看看我們前面定義的Animate類。我們可以給每一個類增加一些特徵,來描述它是什麼和它能做什麼。特徵就是一個類家族中的所有成員(還有它的子類)共同的特點。

例如,所有animals(動物)有什麼共同點?隨便說幾個:它們都要呼吸,它們都會動和吃東西。那麼mammals(哺乳動物)呢?哺乳動物都給它們的孩子餵奶。而且它們也呼吸、會動和吃東西。我們知道長頸鹿從高高的樹頂上吃葉子,然後它們和其他的哺乳動物一樣,也給孩子餵奶、呼吸、會動和吃東西。我們把這些特徵加到樹狀圖上後,就得到了圖8-2所示的結果。


8-2 在樹狀圖上添加特徵

可以把這些特徵想像成是一些動作,或者說函數,也就是那個類的物件能做的事情。

我們用def關鍵字在類中添加函數。所以Animals類就是這樣的:


在這一列代碼中的第一行,我們像往常一樣定義了類,但並沒有在接下來的那一行使用pass關鍵字,而是定義了一個叫breathe(呼吸)的函數,並且給了他一個參數:self。這個self參數是用來從類中的一個函式呼叫類中(還有父類中)的另一個函數的。我們稍後會看到如何使用這個參數。

在下一行,pass關鍵字告訴Python我們暫時不提供更多的資訊,因為暫時我們什麼事也不想讓它做。然後我們加上了函數move(移動)和eat_food(吃食物),它們也是暫時什麼都不做。我們很快就會重建這些類並在函數裡放進一些合適的代碼。這是常見的編寫程式的方法。通常,程式師會先創建類,而其中的函數什麼也不做。先通過這種方式找出這個類應該做的事情,而不是馬上進入到每個函數的細節中去。


我們也可以給其他的兩個類加上函數:MammalsGiraffes。每個類都能使用它父類的特徵(函數)。這意味著你不需要把一個類寫得很複雜。你可以把函數放在這一特徵最早出現的父類中。(這是個讓你的類保持簡單和容易理解的好辦法。)


8.1.5
 為什麼使要用類和物件

我們已經給類加上了函數,但是到底為什麼要使用類和物件?為什麼不是簡單地寫普通的breathemoveeat_food等這些函數?

要回答這個問題,我們要利用一下那個叫Reginald的長頸鹿,就是之前我們創建的Giraffes類的那個物件,像這樣:


因為reginald是一個物件,我們可以調用(或者說運行)它屬於的類(Giraffes類)和它的父類所提供的函數。我們用點運算子“.”和函數的名字來調用物件的函數。要讓長頸鹿Reginald移動或者吃東西,我們可以這樣調用函數。


我們假設Reginald有一個叫Harold的長頸鹿朋友。讓我們創建另一個叫haroldGiraffes物件:


因為我們使用了物件和類,我們可以通過運行move函數來準確地告訴Python我們所指的到底是哪一隻長頸鹿。例如,如果我們想讓Harold移動而Reginald則留在原地,我們可以用harold物件來調用move函數,像這樣:


在這個例子中,只有Harold會移動。

讓我們稍稍改一改這些類,讓結果更明顯。我們要給每個函數加上print語句,而不是只用pass



現在當我們創建了reginaldharold物件並調用它們的函數時,我們可以看到事情發生的過程:


在前兩行中,我們創建了變數reginaldharold,它們是Giraffes類的物件。接下來,我們調用了reginaldmove函數,Python馬上在下一行列印出移動中。我們用同樣的方法調用haroldeat_leaves_from_trees函數,Python列印出在吃樹葉。如果這些是真的長頸鹿而不是電腦中的物件的話,其中一個長頸鹿會在走,另一個在吃東西。


8.1.6
 畫圖中的物件與類

讓我們用更圖形化一點的手段來討論物件和類如何?那就讓我們回到第4章裡我們玩過的那個turtle模組吧。

當我們使用turtle.Pen()時,Python就創建了一個由turtle模組所提供的Pen類的物件(類似於前一節裡的reginaldharold對象)。我們可以創建兩個海龜物件(分別叫AveryKate),就像創建了兩個長頸鹿一樣:


每個海龜物件(averykate)都屬於Pen類。

接下來你就會看到物件強大的地方了。既然創建了這兩個海龜物件,我們就可以對其中每一個來調用它的函數,然後它們會分別畫圖。試試這個:


有了這一系列的指令,我們告訴Avery向前移動50個圖元,向右轉90度,然後再向前移動20個圖元,結束時頭朝下。記住,海龜一開始總是頭朝右的。

現在該移動Kate了。


我們讓Kate向左轉90度,然後向前移動100個圖元,因此它結束時頭朝上。

畫到這裡,我們得到了一條線,它兩端的箭頭指向不同的方向,每個箭頭都代表一個不同的海龜物件:Avery指向下,Kate指向上。如圖8-3所示。


8-3 繪圖結果

現在,讓我們增加一個叫Jacob的海龜,然後移動它,不需要打擾到Kate或者Avery



首先,我們創建一個新的Pen物件,叫jacob,然後我們讓它向左轉180度,然後讓它向前移動80個圖元。我們的畫面裡現在有三個海龜,結果如圖8-4所示。


8-4 添加新對象後的繪圖結果

記住,每次我們調用turtle.Pen()來創建一個海龜,它都是一個新的、獨立的物件。每個物件仍是Pen類的一個實例,每個物件都可以調用同樣的函數。但因為我們使用了物件,我們可以分別移動每個海龜。就像獨立的長頸鹿物件(ReginaldHarold)一樣,AveryKate,還有Jacob是獨立的海龜物件。如果我們創建一個和現在物件同名的變數的話,舊的物件不一定消失。自己試試吧:創建另一個Kate海龜然後隨便移動它一下。

8.2 物件和類的另一些實用功能

類和物件是給函數分組的好辦法。他們還説明我們把程式分成小段來分別思考。

例如,你可以想像一個相當大的軟體應用程式,比如文書處理軟體或者3D電腦遊戲。對大多數人來講幾乎不可能整個地來理解這麼大的程式,因為代碼實在太多了。但是如果把這個龐大的程式分成小的片段,那麼每一塊理解起來就更容易了。

(當然,你得懂得那種程式設計語言才行!)

當寫大型的程式時,把它拆解開還使得你可以把工作分給多個程式師來共同完成。最複雜的那些程式(比方說你的流覽器)是由很多人,或者說很多組人,同時在世界各地分工寫出的。


想像一下,現在我們想擴展這一章裡我們創建的一些類(AnimalsMammals,還有Giraffes),但是工作量太大,我們想找朋友來幫忙。我們可以這樣分工,一個人寫Ainmals類,另一個寫Mammals類,還有一個人寫Giraffes類。

8.2.1 函數繼承

有些讀者可能已經注意到了,那個寫Giraffes類的人很幸運,因為任何寫Animals類和Mammals類的人所寫的函數都可以被Giraffes類使用。Giraffes繼承inherit)了Mammals類,而Mammals類又繼承於Animals類。換句話說,如果我們創建一個長頸鹿物件,我們可以使用Giraffes類中定義的函數,也可以使用MammalsAnimals類中定義的函數。因為同樣的機制,依此類推,如果我們創建一個哺乳動物(mammal)物件,我們可以用Mammals類中定義的函數,也可以使用它的父類Animals中的函數。

再來看一下AnimalsMammals,還有Giraffes類之間的關係。Animals類是Mammals類的父類,而Mammals類是Giraffes的父類。如圖8-5所示。


8-5 函數的繼承

儘管Reginald是一個Giraffes類的物件,我們仍然可以調用Animals類中定義的move函數,因為任何在父類中定義的函數在子類中都可以用:


實際上,reginald物件可以使用所有在AnimalsMammals類中定義的函數,因為這些函數已經被繼承過來了:


8.2.2
 從函數裡調用其他函數

當我們調用一個物件的函數時,我們要使用這個物件的變數名。例如,下面是調用長頸鹿Reginaldmove函數的方法:


想要從Giraffes類的某個函數中調用move函數,我們則要用到self參數。self參數可以用來從類中的一個函式呼叫另外一個函數。例如,假設我們要給Giraffes類增加一個叫find_food的函數的話:


現在我們創建了一個由另外兩個函數組成的函數,這在編寫程式時很常見。通常,你會寫個做些有意義的事情函數,然後在另一個函數內使用它。(在第13章的遊戲程式裡我們會用到這一點,來寫一個更複雜的函數。)

讓我們用self來給Giraffes類增加一些函數:


我們在Giraffes類定義的eat_leaves_from_treesdance_a_jig函數中使用父類Animals類中的eat_foodmove函數,因為它們都是繼承函數。這樣我們就增加一些調用其他函數的函數,當我們創建這些類的物件後,我們可以調用一個函數卻不止做一件事情。下面你可以看到當我們調用dance_a_jig函數時發生了什麼(長頸鹿移動了4次,也就是說列印了4移動中)。



8.3
 初始化對象

當我們創建物件時,有時我們會設置一些值以便將來使用(這些值也叫屬性property)。當我們初始化物件時,我們是在為將來使用它做準備。

比方說,假設我們想在創建長頸鹿物件時設置在它身上斑點的數量,這件事要在初始化時做。要做到這一點,我們要創建立一個__init__函數(請注意,兩邊各有兩個底線,一共是四個)。

這是Python類裡的一種特殊類型的函數,並且只能叫這個名字。這個__init__函數是在物件被創建的同時就設置它的屬性的一種方法,Python會在我們創建新物件時自動調用這個函數。下面是個例子:


首先,用def __init__(self, spots):我們定義了一個有兩個參數的__init__函數,參數分別是selfspots。和其他我們定義在類中的函數一樣,init函數也需要把self當成第一個參數。接下來我們把參數spots設置給self參數的一個叫giraffe_spots的物件變數(也就是它的屬性),寫成的代碼是 self.giraffe_spots = spots。你可以把這行代碼想像成是:把參數spots的值(用物件變數giraffe_spots)保存下來以後用。就像一個在類中的函數用self參數來調用類中的另一個函數一樣,類裡的變數也用self來關聯。(self自己的意思。)

接下來,如果我們創建兩個新的長頸鹿物件(OzwaldGertrude)並顯示它們的斑點數,你就可以看到初始化函數是如何工作的了:


首先,我們創建一個Giraffes類的實例,使用100作為參數。這樣做的效果是調用了__init__函數並用100作為spots參數的值。接下來,我們再創建另一個Giraffes類的實例,這次用150。最後,我們列印出每個長頸鹿物件的物件變數giraffe_spots,我們將看到結果分別為100150。果然好用!

切記,當我們創建一個類的物件時,比方說上面的ozwald,我們可以用點運算子和變數或函數的名字來訪問我們想用的變數或函數(例如ozwald.giraffe_spots)。但是當我們在一個類的內部創建函數時,我們用self參數來指向這些變數或函數(如self.giraffe_spots)。

8.4 你學到了什麼

在這一章裡,我們創建了一系列事物的類,並用這些類來生成類的物件(也叫實例)。你學會了子類是如何繼承父類中的函數的,還有儘管兩個對象屬於同一個類,他們並不一定是一樣的。例如,一個長頸鹿物件身上的斑點數可以與眾不同。你學到了如何調用物件中的函數,還有物件變數是一種把值保存到物件中的方法。最後,我們在函數中用self參數來指向其他的函數和變數。這些概念是Python的基礎,在本書其餘部分你會經常遇到。

8.5 程式設計小測驗

你越是多用,就越會感覺到這一章中的概念很有意義。嘗試一下下面的這些例子吧。答案可以在網站http://python-for-kids.com/ 上找到。

#1:長頸鹿亂舞

Giraffes類增加函數來讓長頸鹿的左(left)、右(right)、前(forward)、後(backward)四隻腳移動。左腳向前移動的函數可以是這樣的:


然後寫一個叫dance的函數來教長頸鹿Reginald跳舞(這個函數會調用你寫的四腳移動的函數)。調用這個新函數的結果就是簡單的舞步:



#2
:海龜叉子

使用四隻Pen物件的海龜來創建圖8-6中側向一邊的叉子(每一行的具體長度並不重要),記住要先引入turtle模組。


8-6 側向一邊的叉子

 

0 留言:

發佈留言