繼承


你建立了一個銀行帳戶類別:
class Account:
def __init__(self, id, name):
self.id = id
self.name = name
self.balance = 0

def deposit(self, amount):
self.balance += amount

def withdraw(self, amount):
if amount <= self.balance:
self.balance -= amount
else:
raise ValueError('餘額不足')

def __str__(self):
return ('Id:\t\t' + self.id +
'\nName:\t\t' + self.name +
'\nBalance:\t' + str(self.balance))

在這個類別中,雖然沒有聲明,但你已經使用了繼承,在Python中,所有類別都繼承自object類 別。上例其實相當於:
class Account(object):
    ...略

在Python中繼承的語法,是在類別名稱旁使用括號表明要繼承的父類別。例如,你為以上的類別建立了一個支票帳戶:
class CheckingAccount(Account):
def __init__(self, id, name):
super(CheckingAccount, self).__init__(id, name) # 呼叫父類別__init__()
self.overdraftlimit = 30000

def withdraw(self, amount):
if amount <= self.balance + self.overdraftlimit:
self.balance -= amount
else:
raise ValueError('超出信用')

def __str__(self):
return (super(CheckingAccount, self).__str__() +
'\nOverdraft limit\t' + str(self.overdraftlimit));

在上例中,你繼承了Account 來定義一個CheckingAccount子類別。如果在子類別中,需要呼叫父類別的某個方法,則可以使用super()指 定類別名稱與物件,這會將目前實例綁定至所指定父類別方法的第一個引數。

在上例中,你重新定義了withdraw() 與__str__()方法,在操作實例方法時,是從子類別開始尋找是否有定義,否則就搜尋父類別中是否有定義方法。所以:
acct = CheckingAccount('E1234', 'Justin Lin')
print(acct, end='\n\n')
acct.deposit(1000)      # 使用 Account 的 deposit() 定義
print(acct, end='\n\n')
acct.withdraw(2000)     # 使用 CheckingAccount 的 withdraw() 定義
print(acct, end='\n\n')

在呼叫acct的 deposit()方法時,由於CheckingAccount並沒有定義,所以呼叫的是Account的deposit(),而呼叫 withdraw()時,則是使用CheckingAccount上有定義的withdraw()。

在Python中,可以進行多重繼承,這個時候要注意搜尋的順序,是從子類別開始,接著是同一階層父類別由左至右搜尋,再至更上層同一階層父類別由左 至右搜尋,直到達到頂層為止。例如:
class A(object):
def method1(self):
print('A.method1')

def method2(self):
print('A.method2')

class B(A):
def method3(self):
print('B.method3')

class C(A):
def method2(self):
print('C.method2')

def method3(self):
print('C.method3')

class D(B, C):
def method4(self):
print('D.method4')

d = D()
d.method4() # 在 D 找到,D.method4
d.method3() # 以 D->B 順序找到,B.method3
d.method2() # 以 D->B->C 順序找到,C.method2
d.method1() # 以 D->B->C->A 順序找到,A.method1

在Python中,類別有個__bases__特性,記錄著所繼承的父類別,__bases__是 個 Tuple,有趣的是,你可以改變__bases__來 動態改變繼承的父類別。例如:
>>> class A:
...     def method(self):
...         print('A method')
...
>>> class B:
...     def method(self):
...         print('B method')
...
>>> class C(A):
...     pass
...
>>> c = C()
>>> c.method()
A method
>>> C.__bases__
(<class '__main__.A'>,)
>>> C.__bases__ = (B,)
>>> c.method()
B method
>>>

在上例中,C原本來繼承自 A類別,透過修改__bases__實際參考的Tuple,C改變繼承B,而尋找特性或方法時,也就改尋找B父類別,因此最後執行的是從B繼承下 來的method()。