abc
— 抽象基類¶
原始碼: Lib/abc.py
此模組提供在 Python 中定義抽象基類 (ABC) 的基礎結構,如 PEP 3119 中所述;有關為何將其新增到 Python 的原因,請參閱該 PEP。(另請參閱 PEP 3141 和 numbers
模組,瞭解基於 ABC 的數字型別層次結構。)
collections
模組有一些派生自 ABC 的具體類;當然,這些類可以進一步派生。此外,collections.abc
子模組有一些 ABC,可用於測試類或例項是否提供特定介面,例如,它是否可雜湊或是否為對映。
此模組提供元類 ABCMeta
用於定義 ABC,以及輔助類 ABC
,用於透過繼承方式定義 ABC。
- class abc.ABC¶
一個輔助類,其元類為
ABCMeta
。使用此輔助類,可以透過簡單地從ABC
派生來建立抽象基類,從而避免有時令人困惑的元類用法,例如from abc import ABC class MyABC(ABC): pass
請注意,
ABC
的型別仍然是ABCMeta
,因此從ABC
繼承需要像使用元類一樣進行常規的預防,因為多重繼承可能導致元類衝突。也可以透過傳遞元類關鍵字並直接使用ABCMeta
來定義抽象基類,例如from abc import ABCMeta class MyABC(metaclass=ABCMeta): pass
在 3.4 版本加入。
- class abc.ABCMeta¶
用於定義抽象基類 (ABC) 的元類。
使用此元類建立 ABC。ABC 可以直接被子類化,然後作為混入類。您還可以將不相關的具體類(甚至是內建類)和不相關的 ABC 註冊為“虛擬子類”——這些類及其後代將被內建的
issubclass()
函式視為註冊 ABC 的子類,但註冊 ABC 不會出現在它們的 MRO(方法解析順序)中,也不會呼叫註冊 ABC 定義的方法實現(甚至不能透過super()
呼叫)。[1]使用
ABCMeta
作為元類建立的類具有以下方法- register(subclass)¶
將 subclass 註冊為此 ABC 的“虛擬子類”。例如
from abc import ABC class MyABC(ABC): pass MyABC.register(tuple) assert issubclass(tuple, MyABC) assert isinstance((), MyABC)
3.3 版中有所改變: 返回註冊的子類,允許作為類裝飾器使用。
3.4 版中有所改變: 要檢測對
register()
的呼叫,可以使用get_cache_token()
函式。
您也可以在抽象基類中重寫此方法
- __subclasshook__(subclass)¶
(必須定義為類方法。)
檢查 subclass 是否被視為此 ABC 的子類。這意味著您可以進一步自定義
issubclass()
的行為,而無需對每個要視為 ABC 子類的類呼叫register()
。(此類方法是從 ABC 的__subclasscheck__()
方法呼叫的。)此方法應返回
True
、False
或NotImplemented
。如果它返回True
,則 subclass 被視為此 ABC 的子類。如果它返回False
,則 subclass 不被視為此 ABC 的子類,即使它通常是。如果它返回NotImplemented
,則子類檢查將繼續使用常規機制。
有關這些概念的演示,請檢視此示例 ABC 定義
class Foo: def __getitem__(self, index): ... def __len__(self): ... def get_iterator(self): return iter(self) class MyIterable(ABC): @abstractmethod def __iter__(self): while False: yield None def get_iterator(self): return self.__iter__() @classmethod def __subclasshook__(cls, C): if cls is MyIterable: if any("__iter__" in B.__dict__ for B in C.__mro__): return True return NotImplemented MyIterable.register(Foo)
ABC
MyIterable
將標準可迭代方法__iter__()
定義為抽象方法。此處給出的實現仍然可以從子類呼叫。get_iterator()
方法也是MyIterable
抽象基類的一部分,但它不需要在非抽象派生類中被重寫。此處定義的
__subclasshook__()
類方法表示,任何在其__dict__
中(或在其基類之一中,透過__mro__
列表訪問)具有__iter__()
方法的類也被視為MyIterable
。最後一行使
Foo
成為MyIterable
的虛擬子類,即使它沒有定義__iter__()
方法(它使用舊式的可迭代協議,透過__len__()
和__getitem__()
定義)。請注意,這不會使get_iterator
作為Foo
的方法可用,因此它是單獨提供的。
abc
模組還提供以下裝飾器
- @abc.abstractmethod¶
一個指示抽象方法的裝飾器。
使用此裝飾器要求類的元類為
ABCMeta
或其派生類。如果一個類的元類派生自ABCMeta
,則除非其所有抽象方法和屬性都被重寫,否則無法例項化該類。抽象方法可以使用任何正常的“super”呼叫機制進行呼叫。abstractmethod()
可用於宣告屬性和描述符的抽象方法。動態地向類新增抽象方法,或嘗試在建立方法或類後修改其抽象狀態,僅支援使用
update_abstractmethods()
函式。abstractmethod()
僅影響使用常規繼承派生的子類;使用 ABC 的register()
方法註冊的“虛擬子類”不受影響。當
abstractmethod()
與其他方法描述符組合使用時,它應該作為最內層裝飾器應用,如以下用法示例所示class C(ABC): @abstractmethod def my_abstract_method(self, arg1): ... @classmethod @abstractmethod def my_abstract_classmethod(cls, arg2): ... @staticmethod @abstractmethod def my_abstract_staticmethod(arg3): ... @property @abstractmethod def my_abstract_property(self): ... @my_abstract_property.setter @abstractmethod def my_abstract_property(self, val): ... @abstractmethod def _get_x(self): ... @abstractmethod def _set_x(self, val): ... x = property(_get_x, _set_x)
為了與抽象基類機制正確互動,描述符必須使用
__isabstractmethod__
將自身標識為抽象。通常,如果用於構成描述符的任何方法都是抽象的,則此屬性應為True
。例如,Python 的內建property
執行與以下程式碼等效的操作class Descriptor: ... @property def __isabstractmethod__(self): return any(getattr(f, '__isabstractmethod__', False) for f in (self._fget, self._fset, self._fdel))
備註
與 Java 抽象方法不同,這些抽象方法可以有實現。此實現可以透過
super()
機制從重寫它的類中呼叫。這對於使用協作式多重繼承的框架中的 super 呼叫終點可能很有用。
abc
模組還支援以下舊版裝飾器
- @abc.abstractclassmethod¶
在 3.2 版本加入。
自 3.3 版棄用: 現在可以使用
classmethod
和abstractmethod()
,使得此裝飾器變得冗餘。內建
classmethod()
的子類,指示抽象類方法。否則,它與abstractmethod()
相似。此特殊情況已棄用,因為
classmethod()
裝飾器在應用於抽象方法時現在被正確識別為抽象的class C(ABC): @classmethod @abstractmethod def my_abstract_classmethod(cls, arg): ...
- @abc.abstractstaticmethod¶
在 3.2 版本加入。
自 3.3 版棄用: 現在可以使用
staticmethod
和abstractmethod()
,使得此裝飾器變得冗餘。內建
staticmethod()
的子類,指示抽象靜態方法。否則,它與abstractmethod()
相似。此特殊情況已棄用,因為
staticmethod()
裝飾器在應用於抽象方法時現在被正確識別為抽象的class C(ABC): @staticmethod @abstractmethod def my_abstract_staticmethod(arg): ...
- @abc.abstractproperty¶
自 3.3 版棄用: 現在可以使用
property
、property.getter()
、property.setter()
和property.deleter()
與abstractmethod()
,使得此裝飾器變得冗餘。內建
property()
的子類,指示抽象屬性。此特殊情況已棄用,因為
property()
裝飾器在應用於抽象方法時現在被正確識別為抽象的class C(ABC): @property @abstractmethod def my_abstract_property(self): ...
上面的例子定義了一個只讀屬性;您也可以透過適當標記一個或多個底層方法為抽象來定義讀寫抽象屬性
class C(ABC): @property def x(self): ... @x.setter @abstractmethod def x(self, val): ...
如果只有某些元件是抽象的,則只需更新這些元件即可在子類中建立具體屬性
class D(C): @C.x.setter def x(self, val): ...
abc
模組還提供以下函式
- abc.get_cache_token()¶
返回當前的抽象基類快取令牌。
該令牌是一個不透明物件(支援相等性測試),用於標識虛擬子類抽象基類快取的當前版本。每次在任何 ABC 上呼叫
ABCMeta.register()
時,令牌都會更改。在 3.4 版本加入。
- abc.update_abstractmethods(cls)¶
一個用於重新計算抽象類的抽象狀態的函式。如果類的抽象方法在建立後已被實現或更改,則應呼叫此函式。通常,此函式應從類裝飾器內部呼叫。
返回 cls,允許作為類裝飾器使用。
如果 cls 不是
ABCMeta
的例項,則不執行任何操作。備註
此函式假定 cls 的超類已更新。它不更新任何子類。
在 3.10 版本加入。
腳註