今天要談的ABC
不是American Born Chinese的'ABC',而是Python的一個模組:abstract base classes
,以及其背後的abstract classes
觀念。
在物件導向程式設計的繼承體系中,有時候父類別定義了一些方法,卻不實作,要將這些方法的內容留給繼承它的子類別來「完成遺願」。父類別這些「只有包子的皮而無包子的餡」的方法,稱為「抽象方法」(abstract methods)。
類別中只要有一個抽象方法,該類別就自動成為「抽象類別」(abstract class)。
抽象類別(方法)可以視作其子類別的「藍圖」(blueprint)。它「規範」了子類別必須要有某些方法,而且最好實作這些方法。
好些物件導向程式語言都有抽象類別機制。然而Python並未在語法層面直接支援,而交由一個在本篇導言提及的abstract base classes
模組實作,這個模組內有一個方法,名稱就叫ABC
(注意全部大寫)。
abc模組以裝飾器@abstractmethod
來「裝飾」父類別中的抽象方法。
以下程式仍以Tree
為父類別,共有四個子類別,分別是Hardwood
, Conifer
, Arbor
, 和Shrub
。先看類別設計的部分:
from abc import ABC, abstractmethod
class Tree(ABC): # 這裡的「樹」採廣義,包括灌木(shrub)。
def __init__(self, breed: str):
self.__breed = breed
@property
def breed(self):
return self.__breed
@abstractmethod
def provide_food(self):
...
@abstractmethod
def help_breathe(self):
...
@abstractmethod
def conserve_water(self):
...
class Hardwood(Tree): # 闊葉樹
# overriding abstract method
def provide_food(self):
print(f'Hi, I am a {__class__.__name__} {self.breed}. I provide food.')
# overriding abstract method
def help_breathe(self):
print(f'Hi, I am a {__class__.__name__} {self.breed}. I help people breathe.')
# overriding abstract method
def conserve_water(self):
print(f'Hi, I am a {__class__.__name__} {self.breed}. I conserve water.')
class Conifer(Tree): # 針葉樹
# overriding abstract method
def provide_food(self):
print(f'Hi, I am a {__class__.__name__} {self.breed}. I provide food.')
# overriding abstract method
def help_breathe(self):
print(f'Hi, I am a {__class__.__name__} {self.breed}. I help people breathe.')
# overriding abstract method
def conserve_water(self):
print(f'Hi, I am a {__class__.__name__} {self.breed}. I conserve water.')
class Arbor(Tree): # 喬木
# overriding abstract method
def provide_food(self):
print(f'Hi, I am an {__class__.__name__} {self.breed}. I provide food.')
# overriding abstract method
def help_breathe(self):
print(f'Hi, I am an {__class__.__name__} {self.breed}. I help people breathe.')
# overriding abstract method
def conserve_water(self):
print(f'Hi, I am an {__class__.__name__} {self.breed}. I conserve water.')
class Shrub(Tree): # 灌木
# overriding abstract method
def provide_food(self):
print(f'Hi, I am a {__class__.__name__} {self.breed}. I provide food.')
# overriding abstract method
def help_breathe(self):
print(f'Hi, I am a {__class__.__name__} {self.breed}. I help people breathe.')
# overriding abstract method
def conserve_water(self):
print(f'Hi, I am a {__class__.__name__} {self.breed}. I conserve water.')
說明:
abc
不須pip install
,直接用之可也。from abc import ABC, abstractmethod
。Tree
須繼承自ABC
(所以ABC顯然也是個類別)。provide_food()
, help_breathe()
, 和conserve_water()
。這三個方法都沒有實作(內容是...Ellipsis
)。@abstractmethod
裝飾器,成為抽象方法。測試程式如下:
tree1 = Hardwood('teak')
tree1.provide_food()
tree1.help_breathe()
tree1.conserve_water()
print()
tree2 = Conifer('cedar')
tree2.provide_food()
tree2.help_breathe()
tree2.conserve_water()
print()
tree3 = Arbor('banyan')
tree3.provide_food()
tree3.help_breathe()
tree3.conserve_water()
print()
tree4 = Shrub('jasmine')
tree4.provide_food()
tree4.help_breathe()
tree4.conserve_water()
輸出如下:
其實父類別也可以不加@abstractmethod
裝飾器:
from abc import ABC, abstractmethod
class Tree(ABC): # 這裡的「樹」採廣義,包括灌木(shrub)。
def __init__(self, breed: str):
self.__breed = breed
@property
def breed(self):
return self.__breed
# @abstractmethod
def provide_food(self):
...
# @abstractmethod
def help_breathe(self):
...
# @abstractmethod
def conserve_water(self):
...
不加@abstractmethod
裝飾器的版本,上面的測試程式的結果是一樣的。
那麼這個裝飾器作用何在?以目前筆者粗淺的了解(肯定有遺漏地方),如果所有方法都不加裝裝飾(也就是該類別不是抽象類別),下面的程式碼:
tree = Tree('cedar')
print(f'{tree.breed = }')
會輸出:
但只要父類別中有任一方法宣告了抽象,例如下面的provide_food()
方法:
from abc import ABC, abstractmethod
class Tree(ABC): # 這裡的「樹」採廣義,包括灌木(shrub)。
def __init__(self, breed: str):
self.__breed = breed
@property
def breed(self):
return self.__breed
@abstractmethod
def provide_food(self):
...
# @abstractmethod
def help_breathe(self):
...
# @abstractmethod
def conserve_water(self):
...
跑同樣的測試碼時:
try:
tree = Tree('cedar')
print(f'{tree.breed = }')
except Exception as e:
print(str(e))
會產生以下的異常(exception):
也就是說,一旦成為抽象類別,就只能被別人繼承,自己無法建立實例了。