自訂類別
用了那麼多 Python 內建的類別 (型態),我們究竟要如何自定義如同 Cat
這樣的類別呢,很簡單:
class Cat:
def __init__(self, name):
self.name = name
def shout(self):
print('Meow')
就是這樣,只要這簡單幾行,我們便可以擁有一個貓的類別,我們來好好檢視一下自定義類別在定義上的結構
class
標明了現在我要定義一個類別,就好像我們使用def
來定義一個函式一樣- 類別的名稱慣例會以大寫開頭,如同本例中的
Cat
- 接著一樣使用冒號,換行後縮排進入 suite
- 接著在類別中我們會定義一連串的 方法 (函式),但是我們總會以
__init__
作為第一個函式 - 每個函式必定以
self
作為第一個參數
以上幾乎就是簡單定義一個類別所需要知道的東西了,但容我們細細來剖析每個環節,首先就是 __init__
這個函式,讀者可能覺得這個名稱也太醜了,使用了雙底線作為開頭和結尾,其實這種以__
開頭和結尾的方法是 Python 物件中的特殊方法,代表了特殊的意義,沒有必要請不要使用這種方式命名你的方法。
這種雙底線開頭結尾的方法又被稱為魔術方法和 dunder function,後者是一個比較正式的名稱。
建構方法
__init__
其實就是 initialize (初始化) 的意思,又稱為 建構方法,每當我們利用類別產生出一個實際物件(實例)的時候,這個方法總會被先呼叫,我們通常會在建構方法中做一些基礎屬性的設定及預設動作的展現。
我們先放下 self
這個東西來思考一個問題,其他的內建型態都可以透過 字面 上的定義來建構物件:
a = 5.7
b = 'yes'
c = [1, 2, 3]
可是像 Cat
這種自訂的類別要怎麼產生實例呢?
我們透過類似函式呼叫的方法就可以了:
my_cat = Cat('Kitty')
在這裡我們好像是呼叫了 Cat
這個類別,提供了貓的名字後就會生成物件。
其實這種方法我們早就見試過了,a = int('5')
實際上就是透過字串作為引數值來生成一個整數(轉換型別其實是這麼一回事)。
建構方法不一定需要
self
以外的參數,如果建構方法只有一個參數self
,那將物件實體化(製造)出來只需要:my_cat = Cat()
但事實上這段程式碼真正的樣貌是:
Cat.__init__(my_cat, 'Kitty')
沒錯!當我們利用類別產生物件時,我們其實是去呼叫類別中的 __init__
方法,並且把參照實例的變數 (my_cat
) 作為第一個參數傳遞給 self
參數,所以說,self
現在會參照 my_cat
而 name
會參照 'Kitty'
:
Cat.__init__(my_cat, 'Kitty')
| /
def __init__(self, name)
代碼 self.name = name
意思就是 my_cat.name = 'Kitty'
,這邊讓我們明白到,self
代表了物件本身。
self 參數
這也是我們要求類別中的方法都要以 self
作為第一個參數的原因,唯有透過 self
,我們才能知道現在是哪個物件在使用這個方法,到底是 my_cat
還是 your_cat
。更深一層的來講,對於所有來自同一類別的實例而言,他們的屬性在電腦中是分開的儲存的,但是方法是共用的,如果沒有 self
來指稱是哪個實例,方法可能會不知道現在是哪個物件要運作。
總而言之,請大家務必為任何方法設定 self
作為第一個參數。
接著來看看 self.name = name
,我們將參數 name
的值傳給 self.name
,在這裡 name
只是一個普通的變數,當該函式執行完畢後,name
就不再存在了(生命週期已經結束),那我們要如何讓貓的名字保留下來(至少在貓活著的時候),並且於函式外面還能存取到呢?
嘿嘿!我們一樣使用 self
,透過設定 self.name
(某實例的名字,如 my_cat.name
),我們可以擁有一個屬於物件的變數,也就是我們一直在講的屬性啦,屬性不但可以跟實例同生共死,還能夠透過實例來存取到,這才是我們要的。
加入
self
作為前綴的變數才會成為屬性,屬性能夠與物件同生共死,也能在類別中任何方法中存取。