Preface :
定義類別,本身就是在進行抽象化,如果一個類別定義時不完整,有些狀態或行為必須留待子類別來具體實現,則它是個抽象類別(Abstract Class)。例如,在定義銀行帳戶時,你也許想將 一些帳戶的共同狀態與行為定義在父類別中 :
- 類別 Account :
- class Account:
- def withdraw(self, amount):
- if amount <= self.balance:
- self.balance -= amount
- else:
- raise ValueError('Not enough balance')
- def __str__(self):
- return ('Id:\t\t' + self.id +
- '\nName:\t\t' + self.name +
- '\nBalance:\t' + str(self.balance))
Abstract Class :
你可以繼承這個類別來實作未完整的定義 :
- 類別 CheckingAccount :
- class CheckingAccount(Account):
- def __init__(self, id, name):
- self.id = id
- self.name = name
- self.balance = 0
- self.overdraftlimit = 30000
- def withdraw(self, amount):
- if amount <= self.balance + self.overdraftlimit:
- self.balance -= amount
- else:
- raise ValueError('Not enough credit')
- def __str__(self):
- return (super(CheckingAccount, self).__str__()+
- '\nOverdraft limit\t' + str(self.overdraftlimit))
- acct = CheckingAccount('E1223','Peter')
- print(acct)
- class Account:
- def __init__():
- raise NotImplementedError("Account is abstract")
- ...略
像 Python 這類的動態語言,沒有 Java 的 abstract 或 interface 這種機制來規範一個類別所需實作的介面,遵循物件之間的協定基本上是開發 人員的自我約束(當然,還得有適當的說明文件). 如果你非得有個方式,強制實現某個公開協定,那該怎麼作?像上面一樣,藉由直譯錯誤是一種方式,實際上視你的需求而定(是否可實例化、子類別是否定義初始化方法等),還有許多模擬的方式,不過在 Python 中,可以使用 Meta class 與 @abstractmethod 來達到規範的需求.
舉個例子來說,您想要設計一個猜數字遊戲,猜數字遊戲的流程大致就是 :
在描述流程輸廓時,並沒有提及如何顯示訊息、沒有提及如何取得使用者輸 入等具體的作法,只是歸納出一些共同的流程步驟 :
- 類別 GuessGame :
- import random
- from abc import ABCMeta, abstractmethod
- class GuessGame(metaclass=ABCMeta):
- @abstractmethod
- def message(self, msg):
- pass
- @abstractmethod
- def guess(self):
- pass
- def go(self):
- self.message(self.welcome)
- number = int(random.random() * 10)
- while True:
- guess = self.guess();
- if guess > number:
- self.message(self.bigger)
- elif guess < number:
- self.message(self.smaller)
- else:
- break
- self.message(self.correct)
如果是個文字模式下的猜數字遊戲,可以將顯示訊息、取得使用者輸入等以文字模式下的具體作法實現出來. 例如 :
- 類別 ConsoleGame :
- class ConsoleGame(GuessGame):
- def __init__(self):
- self.welcome = "歡迎"
- self.prompt = "輸入數字:"
- self.correct = "猜中了"
- self.bigger = "你猜的比較大"
- self.smaller = "你猜的比較小"
- def message(self, msg):
- print(msg)
- def guess(self):
- return int(input(self.prompt))
如果子類別忘了實作某個方法,則該子類別仍被視為一個抽象類別,如果嘗試實例化抽象類別就會引發錯誤。例如若忘了實作 message(),就會發生以下錯誤 :
所以,如果你真的想要模擬Java中interface的作用,則可以定義一個抽象類別,完全沒有實作的方法即可. 例如 :
- import random
- from abc import ABCMeta, abstractmethod
- class Flyer(metaclass=ABCMeta): # 就像是Java中的interface
- @abstractmethod
- def fly(self):
- pass
- class Bird:
- pass
- class Sparrow(Bird, Flyer): # 就像Java中繼承Bird類別並實作Flyer介面
- def fly(self):
- print('麻雀飛')
- s = Sparrow()
- s.fly()
* Python Standard Library: abc — Abstract Base Classes
沒有留言:
張貼留言