abc — 抽象基類

原始碼: Lib/abc.py


此模組為在 Python 中定義抽象基類 (ABC) 提供了基礎架構,如 PEP 3119 中所述;請參閱 PEP 以瞭解為什麼將其新增到 Python 中。(另請參閱 PEP 3141 以及關於基於 ABC 的數字型別層次結構的 numbers 模組。)

collections 模組有一些派生自 ABC 的具體類;當然,這些類可以進一步派生。此外,collections.abc 子模組有一些 ABC,可用於測試類或例項是否提供了特定的介面,例如,它是否是可雜湊的或是否是對映

此模組提供用於定義 ABC 的元類 ABCMeta 和輔助類 ABC,以透過繼承來替代定義 ABC

class abc.ABC

一個以 ABCMeta 作為其元類的輔助類。 使用此類,可以透過簡單地從 ABC 派生來建立抽象基類,從而避免有時令人困惑的元類用法,例如

from abc import ABC

class MyABC(ABC):
    pass

請注意,ABC 的型別仍然是 ABCMeta,因此,從 ABC 繼承需要通常的關於元類用法的預防措施,因為多重繼承可能會導致元類衝突。 也可以透過傳遞 metaclass 關鍵字並直接使用 ABCMeta 來定義抽象基類,例如

from abc import ABCMeta

class MyABC(metaclass=ABCMeta):
    pass

3.4 版本中新增。

class abc.ABCMeta

用於定義抽象基類 (ABC) 的元類。

使用此元類建立 ABC。 ABC 可以直接子類化,然後充當 mix-in 類。 您還可以將不相關的具體類(甚至是內建類)和不相關的 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() 機制呼叫此實現。這對於在框架中使用協作多重繼承的情況下,作為超級呼叫的終點非常有用。

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 版本新增。

腳註