# `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__()` 方法中呼叫。）

    此方法必須回傳 `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` 沒有定義 `__iter__()` 方法（它使用了以 `__len__()` 和 `__getitem__()` 所定義的舊式可疊代物件協定），最末一行使其成為 `MyIterable` 的一個虛擬子類別。請注意這不會使 `get_iterator` 成為 `Foo` 的一個可用方法，所以它是需要被另外提供的。

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

@abc.abstractmethod

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

  類別的元類別是 `ABCMeta` 或是從該類別衍生才能使用此裝飾器。一個具有衍生自 `ABCMeta` 之元類別的類別不可以被實例化，除非它全部的抽象方法和特性均已被覆寫。抽象方法可透過任何一般的 'super' 呼叫機制來呼叫。`abstractmethod()` 可被用於為特性和描述器宣告的抽象方法。

  僅在使用 `update_abstractmethods()` 函式時，才能夠動態地為一個類別新增抽象方法，或者嘗試在方法或類別被建立後修改其抽象狀態。`abstractmethod()` 只會影響使用常規繼承所衍生出的子類別；透過 ABC 的 `register()` 方法註冊的「虛擬子類別」不會受到影響。

  當 `abstractmethod()` 與其他方法描述器 (method descriptor) 配合應用時，它應被當最內層的裝飾器，如以下用法範例所示：

      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)

  為了能正確地與 ABC 機制實作相互操作，描述器必須使用 `__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()` 機制來呼叫。這在使用協作多重繼承 (cooperative multiple-inheritance) 的框架中，可以被用作 super 呼叫的一個端點 (end-point)。

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

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

  在 3.4 版被加入.

abc.update_abstractmethods(cls)

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

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

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

  備註

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

  在 3.10 版被加入.

註腳

[1]

C++ 程式設計師需要注意到 Python 中虛擬基底類別的概念和 C++ 中的並不相同。