定義類別


在Python中要定義類別非常的簡單,例如你可以定義一個帳戶(Account)類別:
class Account:
pass

def deposit(acct, amount):
if amount <= 0:
raise ValueError('must be positive')
acct.balance += amount

def withdraw(acct, amount):
if amount <= acct.balance:
acct.balance -= amount
else:
raise RuntimeError('balance not enough')

acct = Account()
acct.number = '123-456-789'
acct.name = 'Justin'
acct.balance = 0

print(acct.number) # 123-456-789
print(acct.name) # Justin

deposit(acct, 100)
print(acct.balance) # 100
withdraw(acct, 50)
print(acct.balance) # 50

上例中,class用來定義一個類別,現在還沒有要定義類別特性(Property)等,所以使用pass先略過。要建立類別的實例(Instance),直接呼叫Account()來建立。建立物件之後,你可以直接在物件上添增特性,透過 . 來存取特性。

上面這個例子的帳號、名稱、餘額是在程式碼中自行建立,並透過函式來作存提款的動作。事實上,你可以將物件建立後的初始化動作,以及會用到的相關操作定義在類別之中。來看看下面這個例子:
class Account:
def __init__(self, number, name):
self.number = number
self.name = name
self.balance = 0

def deposit(self, amount):
if amount <= 0:
raise ValueError('must be positive')
self.balance += amount

def withdraw(self, amount):
if amount <= self.balance:
self.balance -= amount
else:
raise RuntimeError('balance not enough')

這個類別已包含幾個Python中定義類別的觀念。首先是:
def __init__(self, number, name):
    self.number = number
    self.name = name
    self.balance = 0

__init__這個特定的名稱,用來定義類別的實例建立之後,要進行的初始化動作。第一個self參數代表建立的類別實例,在Python中,實例可操作的方法,第一個參數必須明確作為接受實例之用,慣例上取名為self名稱。__init__之後則可指定初始化時所必須給定的資料。在上面的例子中,會在建立的實例上增加number、name與balance特性。你可以用以下方式來建構物件:
acct = Account('123-456-789', 'Justin')

每個建構出來的Account實例,都會擁有自己的資料狀態,可以直接透過物件及 . 來存取特性:
print(acct.number)    # 123-456-789
print(acct.name)      # Justin

再來看到方法(Method)定義:
def deposit(self, amount):
    if amount <= 0:
        raise ValueError('must be positive')
    self.balance += amount
       
def withdraw(self, amount):
    if amount <= self.balance:
        self.balance -= amount
    else:
        raise RuntimeError('balance not enough')

同樣的道理,在Python中,實例可操作的方法,第一個參數必須明確作為接受實例之用,之後若要在方法中存取物件特性或呼叫物件方法,則必須明確以傳入的物件搭配.運算,例如self.balance這樣的方式。以下則是呼叫方法的幾個例子:
acct.deposit(100)
print(acct.balance)   # 100
acct.withdraw(50)
print(acct.balance)   # 50

在Python中,函式是物件,對於定義在類別中的方法也是一種函式,自然你也可以將之指定給別的變數。例如:
acct1 = Account('123-456-789', 'Justin')
deposit1 = acct1.deposit
withdraw1 = acct1.withdraw
deposit1(100)
withdraw1(50)
print(acct1.balance)

acct2 = Account('987-654-321', 'Momor')
deposit2 = acct2.deposit
withdraw2 = acct2.withdraw
deposit2(200)
withdraw2(100)
print(acct2.balance)

acct1.deposit = acct2.deposit
acct1.deposit(1000)      # 你其實是將錢存到 acct2 去
print(acct1.balance)     # 50
print(acct2.balance)     # 1100

上面這個例子其實也指出了,在Python中,方法總是跟隨著物件而建立,而self是綁定為所建立的物件,別以為將方法指定給另一個物件可以欺騙Python,就如上面的例子中,倒數第三行的結果,其實是將錢存到acct2所參考的物件。