abc
--- 抽象基底類別¶
原始碼:Lib/abc.py
如同在PEP 3119 中所述,該模組提供了在 Python 中定義抽象基底類別 (ABC) 的基礎元件;若想瞭解為什麼需要在 Python 中增加這個模組,請見 PEP 文件。(也請見PEP 3141 以及numbers
模組以瞭解基於 ABC 的數字型別階層關係。)
collections
模組中有一些衍生自 ABC 的具體類別;當然這些類別還可以進一步衍生出其他類別。此外,collections.abc
子模組中有一些 ABC 可被用於測試一個類別或實例是否提供特定介面,例如它是否可雜湊 (hashable) 或它是否為對映。
該模組提供了一個用來定義 ABC 的元類別 (metaclass)ABCMeta
和另一個以繼承的方式定義 ABC 的工具類別ABC
:
- classabc.ABC¶
一個使用
ABCMeta
作為元類別的工具類別。抽象基底類別可以透過自ABC
衍生而建立,這就避免了在某些情況下會令人混淆的元類別用法,用法如以下範例:fromabcimportABCclassMyABC(ABC):pass
注意
ABC
的型別仍然是ABCMeta
,因此繼承ABC
仍然需要關注使用元類別的注意事項,如多重繼承可能會導致元類別衝突。當然你也可以傳入元類別關鍵字並直接使用ABCMeta
來定義一個抽象基底類別,例如:fromabcimportABCMetaclassMyABC(metaclass=ABCMeta):pass
在 3.4 版被加入.
- classabc.ABCMeta¶
用於定義抽象基底類別(ABC)的元類別。
使用該元類別以建立一個 ABC。一個 ABC 可以像 mix-in 類別一樣直接被子類別繼承。你也可以將不相關的具體類別(甚至是內建類別)和 ABC 註冊為「虛擬子類別 (virtual subclass)」 —— 這些類別以及它們的子類別會被內建函式
issubclass()
識別為已註冊 ABC 的子類別,但是該 ABC 不會出現在其 MRO(Method Resolution Order,方法解析順序)中,由該 ABC 所定義的方法實作也不可呼叫(即使透過super()
呼叫也不行)。[1]使用
ABCMeta
作為元類別建立的類別含有以下的方法:- register(subclass)¶
將子類別註冊為該 ABC 的「抽象子類別」,例如:
fromabcimportABCclassMyABC(ABC):passMyABC.register(tuple)assertissubclass(tuple,MyABC)assertisinstance((),MyABC)
在 3.3 版的變更:回傳已註冊的子類別,使其能夠作為類別裝飾器。
在 3.4 版的變更:你可以使用
get_cache_token()
函式來檢測對register()
的呼叫。
你也可以覆寫 (override) 虛擬基底類別中的這個方法:
- __subclasshook__(subclass)¶
(必須定義為類別方法。)
檢查subclass 是否該被認為是該 ABC 的子類別,也就是說你可以直接自訂
issubclass()
的行為,而不用對於那些你希望定義為該 ABC 的子類別的類別都個別呼叫register()
方法。(這個類別方法是在 ABC 的__subclasscheck__()
方法中呼叫。)此方法必須回傳
True
、False
或是NotImplemented
。如果回傳True
,subclass 就會被認為是這個 ABC 的子類別。如果回傳False
,subclass 就會被判定並非該 ABC 的子類別,即便正常情況應如此。如果回傳NotImplemented
,子類別檢查會按照正常機制繼續執行。
為了對這些概念做一演示,請見以下定義 ABC 的範例:
classFoo:def__getitem__(self,index):...def__len__(self):...defget_iterator(self):returniter(self)classMyIterable(ABC):@abstractmethoddef__iter__(self):whileFalse:yieldNonedefget_iterator(self):returnself.__iter__()@classmethoddef__subclasshook__(cls,C):ifclsisMyIterable:ifany("__iter__"inB.__dict__forBinC.__mro__):returnTruereturnNotImplementedMyIterable.register(Foo)
ABC
MyIterable
定義了作為抽象方法的一個標準疊代方法__iter__()
。這裡給定的實作仍可在子類別中被呼叫。get_iterator()
方法也是MyIterable
抽象基底類別的一部分,但它不必被非抽象衍生類別覆寫。這裡定義的
__subclasshook__()
類別方法說明任何在其__dict__
(或在其透過__mro__
列表存取的基底類別) 中具有__iter__()
方法的類別也都會被視為MyIterable
。最後,即使
Foo
沒有定義__iter__()
方法(它使用了以__len__()
和__getitem__()
所定義的舊式可疊代物件協定),最末一行使其成為MyIterable
的一個虛擬子類別。請注意這不會使get_iterator
成為Foo
的一個可用方法,所以它是需要被另外提供的。
abc
模組也提供了這些裝飾器:
- @abc.abstractmethod¶
用於表示抽象方法的裝飾器。
類別的元類別是
ABCMeta
或是從該類別衍生才能使用此裝飾器。一個具有衍生自ABCMeta
之元類別的類別不可以被實例化,除非它全部的抽象方法和特性均已被覆寫。抽象方法可透過任何一般的 'super' 呼叫機制來呼叫。abstractmethod()
可被用於為特性和描述器宣告的抽象方法。僅在使用
update_abstractmethods()
函式時,才能夠動態地為一個類別新增抽象方法,或者嘗試在方法或類別被建立後修改其抽象狀態。abstractmethod()
只會影響使用常規繼承所衍生出的子類別;透過 ABC 的register()
方法註冊的「虛擬子類別」不會受到影響。當
abstractmethod()
與其他方法描述器 (method descriptor) 配合應用時,它應被當最內層的裝飾器,如以下用法範例所示:classC(ABC):@abstractmethoddefmy_abstract_method(self,arg1):...@classmethod@abstractmethoddefmy_abstract_classmethod(cls,arg2):...@staticmethod@abstractmethoddefmy_abstract_staticmethod(arg3):...@property@abstractmethoddefmy_abstract_property(self):...@my_abstract_property.setter@abstractmethoddefmy_abstract_property(self,val):...@abstractmethoddef_get_x(self):...@abstractmethoddef_set_x(self,val):...x=property(_get_x,_set_x)
為了能正確地與 ABC 機制實作相互操作,描述器必須使用
__isabstractmethod__
將自身標識為抽象的。一般來說,如果被用於組成描述器的任一方法是抽象的,則此屬性應當為True
。 例如,Python 的內建property
所做的就等價於:classDescriptor:...@propertydef__isabstractmethod__(self):returnany(getattr(f,'__isabstractmethod__',False)forfin(self._fget,self._fset,self._fdel))
備註
不同於 Java 抽象方法,這些抽象方法可能具有一個實作。這個實作可在覆寫它的類別上透過
super()
機制來呼叫。這在使用協作多重繼承 (cooperative multiple-inheritance) 的框架中,可以被用作 super 呼叫的一個端點 (end-point)。
abc
模組還支援下列舊式裝飾器:
- @abc.abstractclassmethod¶
在 3.2 版被加入.
在 3.3 版之後被棄用:現在可以讓
classmethod
配合abstractmethod()
使用,使得此裝飾器變得冗餘。內建
classmethod()
的子類別,表示為一個抽象類別方法。在其他方面它都類似於abstractmethod()
。這個特例已被棄用,因為現在當
classmethod()
裝飾器應用於抽象方法時已會被正確地標識為是抽象的:classC(ABC):@classmethod@abstractmethoddefmy_abstract_classmethod(cls,arg):...
- @abc.abstractstaticmethod¶
在 3.2 版被加入.
在 3.3 版之後被棄用:現在可以讓
staticmethod
配合abstractmethod()
使用,使得此裝飾器變得冗餘。內建
staticmethod()
的子類別,表示為一個抽象靜態方法。在其他方面它都類似於abstractmethod()
。這個特例已被棄用,因為現在當
staticmethod()
裝飾器應用於抽象方法時已會被正確地標識為是抽象的:classC(ABC):@staticmethod@abstractmethoddefmy_abstract_staticmethod(arg):...
- @abc.abstractproperty¶
在 3.3 版之後被棄用:現在可以讓
property
、property.getter()
、property.setter()
和property.deleter()
配合abstractmethod()
使用,使得此裝飾器變得冗餘。內建
property()
的子類別,表示為一個抽象特性。這個特例已被棄用,因為現在當
property()
裝飾器應用於抽象方法時已會被正確地標識為是抽象的:classC(ABC):@property@abstractmethoddefmy_abstract_property(self):...
上面的例子定義了一個唯讀特性;你也可以透過適當地將一個或多個底層方法標記為抽象的來定義可讀寫的抽象特性:
classC(ABC):@propertydefx(self):...@x.setter@abstractmethoddefx(self,val):...
如果只有某些元件是抽象的,則只需更新那些元件即可在子類別中建立具體的特性:
classD(C):@C.x.setterdefx(self,val):...
abc
模組也提供了這些函式:
- abc.get_cache_token()¶
回傳目前 ABC 快取令牌 (cache token)。
此令牌是一個(支援相等性測試的)不透明物件 (opaque object),用於為虛擬子類別標識抽象基底類別快取的目前版本。此令牌會在任何 ABC 上每次呼叫
ABCMeta.register()
時發生更改。在 3.4 版被加入.
- abc.update_abstractmethods(cls)¶
重新計算一個抽象類別之抽象狀態的函式。如果一個類別的抽象方法在建立後被實作或被修改,則應當呼叫此函式。通常此函式應在一個類別裝飾器內部被呼叫。
回傳cls,使其能夠用作為類別的裝飾器。
如果cls 不是
ABCMeta
的實例則不做任何操作。備註
此函式會假定cls 的超類別 (superclass) 已經被更新。它不會更新任何子類別。
在 3.10 版被加入.
註腳
[1]C++ 程式設計師需要注意到 Python 中虛擬基底類別的概念和 C++ 中的並不相同。