物件導向程式設計 - 入門篇
「物件導向程式設計(Object-Oriented Programming, OOP)」這個名詞聽起來好像很厲害,但它跟前面函數章節曾經提過的「函數式程式設計」一樣,都是一種程式設計的範式(Paradigm),OOP 主要是藉由「物件」這種擬人、擬物化的方式,用我們比較容易想像的方式來組織、管理程式碼。
什麼是物件?
這個問題如果問不同行業的人會有不同的答案,例如對於房仲業的從業人員來說,物件指的可能是一棟房子,但對軟體工程師來說,物件通常指的是一小塊記憶體位置,裡面包含資料和程式碼的資料結構。
在現實生活中,路上跑的車子、天上飛的鳥,同樣都是人類的你我,這種看得到、摸得到的都可通稱為物件,白話的說,物件就是「東西(Thing)」。物件會有「狀態」跟「行為」,例如我本人有「黑色頭髮」、「黃色皮膚」、「年紀 18 歲(?)」等狀態,也有「吃飯」、「睡覺」、「走路」、「講話」等行為。
物件就是一個東西,這個東西身上會有一些狀態跟行為:
- 狀態:髮色、膚色、年齡、身體、體重...等名詞
- 行為:走路、跑步、吃飯、講話、睡覺、追 劇、打電動、寫程式...等動詞
所以:
物件(Object) = 狀態(State) + 行為(Behavior)
狀態,就是物件的「屬性(Attribute)」,而行為就是物件的「方法(Method)」,也就是寫在物件裡的函數。
為了讓大家更容易理解、學習程式,許多程式語言都有引進「擬人化」或「擬物化」的設計,讓我們可以用一般人更容易想像的方式來組織或管理程式碼,這就是所謂的物件導向程式設計。問題是,我們怎麼在 Python 建立或使用物件?這就得先跟大家介紹一下「類別(Class)」。
類別與物件
我記得我以前自學程式的時候,每次看書上講到關於物件導向的介紹,常會說類別就像建築物或是飛機的「藍圖」一樣,透過藍圖就能建立很多物件。但我不會蓋房子也不會造飛機,這藍圖的比喻對我來說有點難想像,所以我改用我們這種平民老百姓更容易遇到、更生活化的範例。不知道大家有沒有在路邊或夜市看過或吃過雞蛋糕,有小貓、小狗、皮卡丘等可愛動物造型,只要把調配好的麵粉糊倒進烤盤,蓋上蓋子,等幾分鐘就有香噴噴又造型可愛的雞蛋糕可以吃了:
以物件導向的專有名詞來說,這個烤盤模具,就是「類別(Class)」的概念。一樣形狀的模具,放一樣的原料 進去,如果沒壓壞的話做出來雞蛋糕的形狀就都會長得一樣。透過烤盤做出來的雞蛋糕,以物件導向程式設計的專有名詞來說會稱之「實體(Instance)」,接下來的章節我都會使用這些名詞。
在 Python 定義類別,使用的關鍵字是 class
:
class Cat:
pass
因為目前還不知道要放什麼內容,所以我先放 pass
卡個位。這樣我們就定義了一個名為 Cat
的類別,不過目前這個類別裡面空空的,沒什麼用途就是了。
建立物件
有類別之後,就可以透過它來產生物件:
kitty = Cat()
nancy = Cat()
這就跟用烤盤模具可以一直做出雞蛋糕一樣,透過 Cat()
類別你想做幾隻貓都沒問題,這裡我先做了兩個 Cat
的實體,分別叫做 kitty
和 nancy
。雖然這兩 個物件都是用 Cat
類別生成的,但它們是不同的物件,就像你跟我一樣雖然都是 人類
這個類別產生的實體,但我們都是不同的個體,雖然有相同的行為,但各自有不同的狀態。
看到這裡,應該也不難發現類別用起來的手感跟呼叫函數有點像。我們可以來看看這些東西長什麼樣子:
>>> kitty
<__main__.Cat object at 0x104c6d160>
>>> nancy
<__main__.Cat object at 0x104c6f0b0>
後面那個 0x104c6d160
或是 0x104c6f0b0
這種看起來像隨機英文數字組合的東西,它代表這顆物件所在的記憶體位置。這兩個數值不一樣,表示這兩顆物件所在的 記憶體位置是不一樣的,也就是說,它們是兩個不同且獨立的個體。
雖然 kitty
跟 nancy
是不同的個體,但都是一種 Cat
:
>>> isinstance(kitty, Cat)
True
>>> isinstance(nancy, Cat)
True
透過內建函數 isinstance()
可以判斷物件是否為某個類別的實體,這裡我們可以看到 kitty
和 nancy
都是 Cat
類別的實體,但不是字串 str
的實體:
>>> isinstance(nancy, str)
False
另外,每個物件,不管是我們自己設計的或是 Python 內建的,一定都是某個類別生出來的,沒有例外。透過 __class__
屬性可以觀察到這顆物件是由哪個類別產生的:
>>> kitty.__class__
<class '__main__.Cat'>
>>> kitty.__class__.__name__
'Cat'