On this page
abc —抽象 Base Class
源代码: Lib/abc.py
该模块提供了在 PEP 3119中概述的用于在 Python 中定义抽象 Base Class(ABC)的基础结构;有关为何将其添加到 Python 的信息,请参阅 PEP。 (有关基于 ABC 的数字的类型层次结构,另请参见 PEP 3141和numbers模块。)
collections模块具有一些从 ABC 派生的具体类;当然,这些可以进一步推导。此外,collections.abc子模块具有一些 ABC,可用于测试类或实例是否提供特定接口,例如,是否可哈希或是否为 Map。
该模块提供了用于定义 ABC 的元类ABCMeta和用于pass继承来替代定义 ABC 的辅助类ABC:
from abc import ABC
class MyABC(ABC):
pass
请注意,ABC的类型仍然是ABCMeta,因此从ABC继承需要有关元类使用的常规预防措施,因为多重继承可能会导致元类冲突。也可以pass传递 metaclass 关键字并直接使用ABCMeta来定义抽象 Base Class,例如:
from abc import ABCMeta
class MyABC(metaclass=ABCMeta):
pass
3.4 版的新Function。
- 类别
abc.
ABCMeta
- 用于定义抽象 Base Class(ABC)的元类。
使用此元类创建一个 ABC。 ABC 可以直接子类化,然后充当混合类。您还可以将不相关的具体类(甚至是内置类)和不相关的 ABC 注册为“虚拟子类” –内置的issubclass()函数会将它们及其后代视为注册 ABC 的子类,但不会注册 ABC 显示在其 MRO(方法解析 Sequences)中,也不会调用由注册 ABC 定义的方法实现(甚至不能passsuper()调用)。 [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()函数。
您还可以在抽象 Base Class 中重写此方法:
__subclasshook__
(* subclass *)- (必须定义为类方法.)
检查* subclass *是否被视为此 ABC 的子类。这意味着您可以进一步自定义issubclass
的行为,而无需在要考虑 ABC 的子类的每个类上调用register()。 (此类方法从 ABC 的__subclasscheck__()
方法调用.)
此方法应返回True
,False
或NotImplemented
。如果返回True
,则* subclass 被视为此 ABC 的子类。如果它返回False
,则 subclass *不会被视为此 ABC 的子类,即使它通常是一个。如果返回NotImplemented
,则使用常规机制 continue 子类检查。
为了演示这些概念,请看以下示例 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
抽象 Base Class 的一部分,但是在非抽象派生类中不必重写它。
此处定义的subclasshook()类方法表示,在其dict(或passmro列表访问的一个 Base Class 之一)中具有iter()方法的任何类也被视为MyIterable
。
最后,最后一行使Foo
成为MyIterable
的虚拟子类,即使它没有定义iter()方法(它使用按照len()和getitem()定义的旧式可迭代协议)。请注意,这不会使get_iterator
作为Foo
的方法可用,因此将单独提供它。
abc模块还提供以下装饰器:
@
abc.
abstractmethod
- 装饰器,指示抽象方法。
使用此装饰器要求该类的元类为ABCMeta或从其派生。除非实例化了所有抽象方法和属性,否则无法实例化具有从ABCMeta派生的元类的类。可以使用任何正常的“超级”调用机制来调用抽象方法。 abstractmethod()可用于语句属性和 Descriptors 的抽象方法。
不支持将动态方法添加到类,或在创建方法或类后try修改其抽象状态。 abstractmethod()仅影响使用常规继承派生的子类。pass ABC 的register()
方法注册的“虚拟子类”不受影响。
abstractmethod()与其他方法 Descriptors 结合使用时,应将其用作最里面的装饰器,如以下用法示例所示:
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)
为了正确地与抽象 Base Class 机制进行互操作,Descriptors 必须使用__isabstractmethod__
将其自身标识为抽象。通常,如果用于构成 Descriptors 的任何方法都是抽象的,则此属性应为True
。例如,Python 的内置property等效于:
class Descriptor:
...
@property
def __isabstractmethod__(self):
return any(getattr(f, '__isabstractmethod__', False) for
f in (self._fget, self._fset, self._fdel))
Note
与 Java 抽象方法不同,这些抽象方法可能具有实现。可以pass覆盖它的类中的super()机制调用此实现。在使用协作式多重继承的框架中,这可用作超级调用的端点。
abc模块还支持以下旧式装饰器:
@
abc.
abstractclassmethod
- 3.2 版中的新Function。
从版本 3.3 开始不推荐使用:现在可以将classmethod与abstractmethod()结合使用,从而使此装饰器变得多余。
内置classmethod()的子类,指示抽象的类方法。否则,它类似于abstractmethod()。
不赞成使用此特殊情况,因为在将_ 装饰器应用于抽象方法时,现在可以正确地将其标识为抽象:
class C(ABC):
@classmethod
@abstractmethod
def my_abstract_classmethod(cls, ...):
...
@
abc.
abstractstaticmethod
- 3.2 版中的新Function。
从版本 3.3 开始不推荐使用:现在可以将staticmethod与abstractmethod()结合使用,从而使此装饰器变得多余。
内置staticmethod()的子类,指示抽象的静态方法。否则,它类似于abstractmethod()。
不赞成使用此特殊情况,因为在将_ 装饰器应用于抽象方法时,现在可以正确地将其标识为抽象:
class C(ABC):
@staticmethod
@abstractmethod
def my_abstract_staticmethod(...):
...
@
abc.
abstractproperty
- 从版本 3.3 开始不推荐使用:现在可以将property,
property.getter()
,property.setter()
和property.deleter()
与abstractmethod()一起使用,从而使此装饰器变得多余。
- 从版本 3.3 开始不推荐使用:现在可以将property,
内置property()的子类,指示抽象属性。
不赞成使用此特殊情况,因为在将_ 装饰器应用于抽象方法时,现在可以正确地将其标识为抽象:
class C(ABC):
@property
@abstractmethod
def my_abstract_property(self):
...
上面的示例定义了一个只读属性;您还可以pass适当地将一个或多个基础方法标记为抽象来定义读写抽象属性:
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模块还提供以下Function:
abc.
get_cache_token
( )- 返回当前抽象 Base Class 缓存令牌。
令牌是一个不透明的对象(支持相等性测试),用于标识虚拟子类的抽象 Base Class 缓存的当前版本。在任何 ABC 上每次呼叫ABCMeta.register()时,令牌都会更改。
3.4 版的新Function。
Footnotes
- [1]
- C 程序员应注意,Python 的虚拟 Base Class 概念与 C 的概念不同。