abc --- 抽象基類?
源代碼: Lib/abc.py
該模塊提供了在 Python 中定義 抽象基類 (ABC) 的組件,在 PEP 3119 中已有概述。查看 PEP 文檔了解為什么需要在 Python 中增加這個模塊。(也可查看 PEP 3141 以及 numbers 模塊了解基于 ABC 的數字類型繼承關系。)
collections 模塊中有一些派生自 ABC 的具體類;當然這些類還可以進一步被派生。此外,collections.abc 子模塊中有一些 ABC 可被用于測試一個類或實例是否提供特定的接口,例如它是否可哈希或它是否為映射等。
該模塊提供了一個元類 ABCMeta,可以用來定義抽象類,另外還提供一個工具類 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)的元類。
使用該元類以創建抽象基類。抽象基類可以像 mix-in 類一樣直接被子類繼承。你也可以將不相關的具體類(包括內建類)和抽象基類注冊為“抽象子類” —— 這些類以及它們的子類會被內建函數
issubclass()識別為對應的抽象基類的子類,但是該抽象基類不會出現在其 MRO(Method Resolution Order,方法解析順序)中,抽象基類中實現的方法也不可調用(即使通過super()調用也不行)。1使用
ABCMeta作為元類創建的類含有如下方法:-
register(subclass)? 將“子類”注冊為該抽象基類的“抽象子類”,例如:
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()的調用。
你也可以在虛基類中重載這個方法。
-
__subclasshook__(subclass)? (必須定義為類方法。)
檢查 subclass 是否是該抽象基類的子類。也就是說對于那些你希望定義為該抽象基類的子類的類,你不用對每個類都調用
register()方法了,而是可以直接自定義issubclass的行為。(這個類方法是在抽象基類的__subclasscheck__()方法中調用的。)該方法必須返回
True,False或是NotImplemented。如果返回True,subclass 就會被認為是這個抽象基類的子類。如果返回False,無論正常情況是否應該認為是其子類,統一視為不是。如果返回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()可被用于聲明特性屬性和描述器的抽象方法。不支持動態添加抽象方法到一個類,或試圖在方法或類被創建后修改其抽象狀態等操作。
abstractmethod()只會影響使用常規繼承所派生的子類;通過 ABC 的register()方法注冊的“虛子類”不會受到影響。當
abstractmethod()與其他方法描述符配合應用時,它應當被應用為最內層的裝飾器,如以下用法示例所示:class C(ABC): @abstractmethod def my_abstract_method(self, ...): ... @classmethod @abstractmethod def my_abstract_classmethod(cls, ...): ... @staticmethod @abstractmethod def my_abstract_staticmethod(...): ... @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 版后已移除: 現在可以讓
classmethod配合abstractmethod()使用,使得此裝飾器變得冗余。內置
classmethod()的子類,指明一個抽象類方法。 在其他情況下它都類似于abstractmethod()。這個特殊情況已被棄用,因為現在
classmethod()當將裝飾器應用于抽象方法時它會被正確地標識為抽象的:class C(ABC): @classmethod @abstractmethod def my_abstract_classmethod(cls, ...): ...
-
@abc.abstractstaticmethod? 3.2 新版功能.
3.3 版后已移除: 現在可以讓
staticmethod配合abstractmethod()使用,使得此裝飾器變得冗余。內置
staticmethod()的子類,指明一個抽象靜態方法。 在其他方面它都類似于abstractmethod()。這個特殊情況已被棄用,因為現在當
staticmethod()裝飾器應用于抽象方法時它會被正確地標識為抽象的:class C(ABC): @staticmethod @abstractmethod def my_abstract_staticmethod(...): ...
-
@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 新版功能.
備注
- 1
C++ 程序員需要注意:Python 中虛基類的概念和 C++ 中的并不相同。
