abc — 抽象基類

原始碼: Lib/abc.py


此模組提供在 Python 中定義抽象基類 (ABC) 的基礎結構,如 PEP 3119 中所述;有關為何將其新增到 Python 的原因,請參閱該 PEP。(另請參閱 PEP 3141numbers 模組,瞭解基於 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__() 方法呼叫的。)

此方法應返回 TrueFalseNotImplemented。如果它返回 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 版棄用: 現在可以使用 classmethodabstractmethod(),使得此裝飾器變得冗餘。

內建 classmethod() 的子類,指示抽象類方法。否則,它與 abstractmethod() 相似。

此特殊情況已棄用,因為 classmethod() 裝飾器在應用於抽象方法時現在被正確識別為抽象的

class C(ABC):
    @classmethod
    @abstractmethod
    def my_abstract_classmethod(cls, arg):
        ...
@abc.abstractstaticmethod

在 3.2 版本加入。

自 3.3 版棄用: 現在可以使用 staticmethodabstractmethod(),使得此裝飾器變得冗餘。

內建 staticmethod() 的子類,指示抽象靜態方法。否則,它與 abstractmethod() 相似。

此特殊情況已棄用,因為 staticmethod() 裝飾器在應用於抽象方法時現在被正確識別為抽象的

class C(ABC):
    @staticmethod
    @abstractmethod
    def my_abstract_staticmethod(arg):
        ...
@abc.abstractproperty

自 3.3 版棄用: 現在可以使用 propertyproperty.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 版本加入。

腳註