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

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 可以像 mix-in 類別一樣直接被子類別繼承。你也可以將不相關的具體類別(甚至是內建類別)和 ABC 註冊為「虛擬子類別 (virtual subclass)」 —— 這些類別以及它們的子類別會被內建函式 issubclass() 識別為已註冊 ABC 的子類別,但是該 ABC 不會出現在其 MRO(Method Resolution Order,方法解析順序)中,由該 ABC 所定義的方法實作也不可呼叫(即使透過 super() 呼叫也不行)。[1]

使用 ABCMeta 作為元類別建立的類別含有以下的方法:

register(subclass)

子類別註冊為該 ABC 的「抽象子類別」,例如:

from abc import ABC

class MyABC(ABC):
    pass

MyABC.register(tuple)

assert issubclass(tuple, MyABC)
assert isinstance((), MyABC)

在 3.3 版的變更: 回傳已註冊的子類別,使其能夠作為類別裝飾器。

在 3.4 版的變更: 你可以使用 get_cache_token() 函式來檢測對 register() 的呼叫。

你也可以覆寫 (override) 虛擬基底類別中的這個方法:

__subclasshook__(subclass)

(必須定義為類別方法。)

檢查 subclass 是否該被認為是該 ABC 的子類別,也就是說你可以直接自訂 issubclass() 的行為,而不用對於那些你希望定義為該 ABC 的子類別的類別都個別呼叫 register() 方法。(這個類別方法是在 ABC 的 __subclasscheck__() 方法中呼叫。)

此方法必須回傳 TrueFalse 或是 NotImplemented。如果回傳 Truesubclass 就會被認為是這個 ABC 的子類別。如果回傳 Falsesubclass 就會被判定並非該 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 沒有定義 __iter__() 方法(它使用了以 __len__()__getitem__() 所定義的舊式可疊代物件協定),最末一行使其成為 MyIterable 的一個虛擬子類別。請注意這不會使 get_iterator 成為 Foo 的一個可用方法,所以它是需要被另外提供的。

abc 模組也提供了這些裝飾器:

@abc.abstractmethod

用於表示抽象方法的裝飾器。

Using this decorator requires that the class's metaclass is ABCMeta or is derived from it. A class that has a metaclass derived from ABCMeta cannot be instantiated unless all of its abstract methods and properties are overridden. The abstract methods can be called using any of the normal 'super' call mechanisms. @abstractmethod may be used to declare abstract methods for properties and descriptors.

Dynamically adding abstract methods to a class, or attempting to modify the abstraction status of a method or class once it is created, are only supported using the update_abstractmethods() function. The @abstractmethod only affects subclasses derived using regular inheritance; "virtual subclasses" registered with the ABC's register() method are not affected.

When @abstractmethod is applied in combination with other method descriptors, it should be applied as the innermost decorator, as shown in the following usage examples:

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)

In order to correctly interoperate with the abstract base class machinery, the descriptor must identify itself as abstract using __isabstractmethod__. In general, this attribute should be True if any of the methods used to compose the descriptor are abstract. For example, Python's built-in @property does the equivalent of:

class Descriptor:
    ...
    @property
    def __isabstractmethod__(self):
        return any(getattr(f, '__isabstractmethod__', False) for
                   f in (self._fget, self._fset, self._fdel))

備註

不同於 Java 抽象方法,這些抽象方法可能具有一個實作。這個實作可在覆寫它的類別上透過 super() 機制來呼叫。這在使用協作多重繼承 (cooperative multiple-inheritance) 的框架中,可以被用作 super 呼叫的一個端點 (end-point)。

abc 模組還支援下列舊式裝飾器:

@abc.abstractclassmethod

在 3.2 版被加入.

自從版本 3.3 後不推薦使用,將會自版本 3.21 中移除。: It is now possible to use @classmethod with @abstractmethod, making this decorator redundant.

A subclass of the built-in classmethod, indicating an abstract classmethod. Otherwise it is similar to @abstractmethod.

This special case is deprecated, as the @classmethod decorator is now correctly identified as abstract when applied to an abstract method:

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

在 3.2 版被加入.

自從版本 3.3 後不推薦使用,將會自版本 3.21 中移除。: It is now possible to use @staticmethod with @abstractmethod, making this decorator redundant.

A subclass of the built-in staticmethod, indicating an abstract staticmethod. Otherwise it is similar to @abstractmethod.

This special case is deprecated, as the @staticmethod decorator is now correctly identified as abstract when applied to an abstract method:

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

自從版本 3.3 後不推薦使用,將會自版本 3.21 中移除。: It is now possible to use @property, @property.getter, @property.setter and @property.deleter with @abstractmethod, making this decorator redundant.

A subclass of the built-in property, indicating an abstract property.

This special case is deprecated, as the @property decorator is now correctly identified as abstract when applied to an abstract method:

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 快取令牌 (cache token)。

此令牌是一個(支援相等性測試的)不透明物件 (opaque object),用於為虛擬子類別標識抽象基底類別快取的目前版本。此令牌會在任何 ABC 上每次呼叫 ABCMeta.register() 時發生更改。

在 3.4 版被加入.

abc.update_abstractmethods(cls)

重新計算一個抽象類別之抽象狀態的函式。如果一個類別的抽象方法在建立後被實作或被修改,則應當呼叫此函式。通常此函式應在一個類別裝飾器內部被呼叫。

回傳 cls,使其能夠用作為類別的裝飾器。

如果 cls 不是 ABCMeta 的實例則不做任何操作。

備註

此函式會假定 cls 的超類別 (superclass) 已經被更新。它不會更新任何子類別。

在 3.10 版被加入.

註腳