29.7. abc
- 抽象基类¶
源代码: Lib / abc.py
此模块提供了用于定义Python中抽象基类(ABCs)的基础结构,如 PEP 3119请参阅PEP为什么这被添加到Python。(有关基于ABC的数字的类型层次结构,请参见 PEP 3141和数字
容器
模块有一些从ABCs派生的具体类;这些当然可以进一步推导出来。此外,collections.abc
子模块具有一些可用于测试类或实例是否提供特定接口的ABC,例如,它是哈希表还是映射。
此模块提供以下类:
- class
abc.
ABCMeta
¶ 用于定义抽象基类(ABC)的元类。
使用此元类创建ABC。ABC可以直接子类化,然后充当混合类。你还可以将不相关的具体类(甚至内建类)和不相关的ABCs注册为“虚拟子类” - 这些及其后代将被内建
issubclass()
函数视为注册ABC的子类,但是注册的ABC不会出现在他们的MRO(方法解析顺序)中,注册的ABC定义的方法实现也不能调用(甚至不能通过super()
)。[1]使用
ABCMeta
的元类创建的类具有以下方法:-
register
(subclass)¶ 将子类注册为此ABC的“虚拟子类”。例如:
from abc import ABCMeta class MyABC(metaclass=ABCMeta): pass MyABC.register(tuple) assert issubclass(tuple, MyABC) assert isinstance((), MyABC)
在版本3.3中更改:返回已注册的子类,以允许用作类装饰器。
在版本3.4中更改:要检测对
register()
的调用,您可以使用get_cache_token()
函数。
您还可以在抽象基类中覆盖此方法:
-
__subclasshook__
(subclass)¶ (必须定义为类方法。)
检查子类是否被认为是此ABC的子类。这意味着您可以进一步自定义
issubclass
的行为,而无需在每个想要考虑ABC子类的类中调用register()
。(此类方法从ABC的__subclasscheck__()
方法调用。)此方法应返回
True
,False
或NotImplemented
。如果返回True
,则子类被认为是此ABC的子类。如果它返回False
,则子类不被认为是此ABC的子类,即使它通常是一个。如果返回NotImplemented
,则子类检查以常用机制继续。
为了演示这些概念,看看这个例子ABC定义:
class Foo: def __getitem__(self, index): ... def __len__(self): ... def get_iterator(self): return iter(self) class MyIterable(metaclass=ABCMeta): @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
抽象基类的一部分,但它不必在非抽象派生类中被覆盖。The
__subclasshook__()
class method defined here says that any class that has an__iter__()
method in its__dict__
(or in that of one of its base classes, accessed via the__mro__
list) is considered aMyIterable
too.最后,最后一行使
Foo
是MyIterable
的虚拟子类,即使它没有定义一个__iter__()
按照__len__()
和__getitem__()
定义的类型可迭代协议。请注意,这不会使get_iterator
作为Foo
的方法可用,因此它是单独提供的。-
- class
abc.
ABC
¶ 一个辅助类,它具有
ABCMeta
作为其元类。使用这个类,可以通过简单地从ABC
派生来创建抽象基类,避免有时会混淆元类使用。注意,
ABC
的类型仍然是ABCMeta
,因此从ABC
继承需要关于元类使用的通常预防措施,因为多重继承可能导致元类冲突。版本3.4中的新功能。
abc
模块还提供以下装饰器:
-
@
abc.
abstractmethod
¶ 装饰器指示抽象方法。
使用这个装饰器需要类的元类是
ABCMeta
或派生自它。具有从ABCMeta
派生的元类的类不能实例化,除非其所有的抽象方法和属性都被覆盖。抽象方法可以使用任何正常的“超级”调用机制来调用。abstractmethod()
可用于声明属性和描述器的抽象方法。动态地向类中添加抽象方法,或者在创建方法或类之后尝试修改抽象状态。
abstractmethod()
仅影响使用常规继承导出的子类;注册到ABC的register()
方法的“虚拟子类”不受影响。当
abstractmethod()
与其他方法描述器组合应用时,应将其应用为最内装饰器件,如以下用法示例所示:class C(metaclass=ABCMeta): @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的内建属性相当于:class Descriptor: ... @property def __isabstractmethod__(self): return any(getattr(f, '__isabstractmethod__', False) for f in (self._fget, self._fset, self._fdel))
注意
与Java抽象方法不同,这些抽象方法可能有一个实现。这个实现可以通过
super()
机制从覆盖它的类中调用。这可以用作使用协作多继承的框架中的超级调用的端点。
-
@
abc.
abstractclassmethod
¶ 内建
classmethod()
的子类,表示抽象类方法。否则它类似于abstractmethod()
。这种特殊情况已被弃用,因为
classmethod()
装饰器在应用于抽象方法时现已正确标识为抽象:class C(metaclass=ABCMeta): @classmethod @abstractmethod def my_abstract_classmethod(cls, ...): ...
版本3.2中的新功能。
自版本3.3后已弃用:现在可以使用
classmethod
与abstractmethod()
,使此装饰器冗余。
-
@
abc.
abstractstaticmethod
¶ 内建
staticmethod()
的子类,表示抽象静态方法。否则它类似于abstractmethod()
。这种特殊情况已被弃用,因为
staticmethod()
装饰器在应用于抽象方法时现在可正确识别为抽象:class C(metaclass=ABCMeta): @staticmethod @abstractmethod def my_abstract_staticmethod(...): ...
版本3.2中的新功能。
自版本3.3后已弃用:现在可以使用
staticmethod
与abstractmethod()
,使此装饰器冗余。
-
@
abc.
abstractproperty
(fget=None, fset=None, fdel=None, doc=None)¶ 内建
property()
的子类,表示一个抽象属性。使用这个函数需要类的元类是
ABCMeta
或派生自它。具有从ABCMeta
派生的元类的类不能实例化,除非其所有的抽象方法和属性都被覆盖。可以使用任何正常的“超级”调用机制来调用抽象属性。这种特殊情况已被弃用,因为
property()
装饰器在应用于抽象方法时现已正确标识为抽象:class C(metaclass=ABCMeta): @property @abstractmethod def my_abstract_property(self): ...
上面的例子定义了一个只读属性;您还可以通过将一个或多个底层方法适当地标记为抽象来定义读写抽象属性:
class C(metaclass=ABCMeta): @property def x(self): ... @x.setter @abstractmethod def x(self, val): ...
如果只有一些组件是抽象的,只有那些组件需要更新以在子类中创建一个具体的属性:
class D(C): @C.x.setter def x(self, val): ...
Deprecated since version 3.3: It is now possible to use
property
,property.getter()
,property.setter()
andproperty.deleter()
withabstractmethod()
, making this decorator redundant.
abc
模块还提供以下功能:
-
abc.
get_cache_token
()¶ 返回当前抽象基类缓存令牌。
令牌是一个不透明的对象(支持相等测试),用于标识虚拟子类的抽象基类缓存的当前版本。令牌随着对任何ABC的每个对
ABCMeta.register()
的调用而改变。版本3.4中的新功能。
脚注
[1] | C ++程序员应该注意Python的虚拟基类概念与C ++不一样。 |