自訂類別

用了那麼多 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_catname 會參照 '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 作為前綴的變數才會成為屬性,屬性能夠與物件同生共死,也能在類別中任何方法中存取。

results matching ""

    No results matching ""