2011年9月10日 星期六

繼承

你建立了一個銀行帳戶類別:

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('C.method4')

d = D()
d.method4() # 在 D 找到,C.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()。

沒有留言:

張貼留言