源码: Lib/abc.py
该模块提供了用于在Python中定义抽象基类(ABC)的基础结构,如 PEP 3119 中所述; 请参阅PEP,了解为何将其添加到Python中。(也可以看看PEP 3141和 numbers模块关于基于ABC的数字类型层次结构的 。)
collections模块有一些来自ABCs的具体类; 当然,这些可以进一步推导出来。此外, collections.abc子模块还有一些ABCs可用于测试类或实例是否提供特定接口,例如,如果是否可哈希,或者它是否是一个映射。
该模块提供了 元类 ABCMeta 用于定义ABCs 和一个辅助类ABC,以通过继承来定义ABCs:
一个辅助类,具有ABCMeta作为其元类。可以通过简单地从ABC创建抽象基类, 避免有时混淆元类使用的方法,例如:
from abc import ABC
class MyABC(ABC):
pass
请注意,type(ABC)
仍然是 ABCMeta,因此继承ABC需要有关元类使用的常规预防措施,因为多重继承可能导致元类冲突。
译者注:使用此种class MyABC(ABC)
抽象基类,子类必须覆写所有的抽象基类的抽象方法和抽象属性,否则会报错,如下面的例子。
# 1.直接使用抽象基类
class MyABC(ABC):
pass
@abstractmethod
def get_name(self):
pass
class Stu(MyABC):
pass
def __init__(self,name):
self.name = name
def __str__(self):
return self.name
print(type(Stu)) #
s = Stu('leng')
# 结果
<class 'abc.ABCMeta'>
Traceback (most recent call last):
File "D:/pyt-code/python/mod/abc1.py", line 18, in <module>
s = Stu('leng')
TypeError: Can't instantiate abstract class Stu with abstract methods get_name
用于定义抽象基类(ABCs)的元类。
通过传递metaclass=ABCMeta
来定义抽象基类,例如:
from abc import ABCMeta
class MyABC(metaclass=ABCMeta):
pass
版本3.4中的新功能。
使用此元类创建ABC。ABC可以直接子类化,然后充当混合类。您还可以将不相关的具体类(甚至是内置类)和不相关的ABC注册为“虚拟子类” - 这些将被内置函数issubclass() 视为ABC注册的子类,但注册ABC将不会显示在他们的MRO(方法解析顺序)中,注册ABC定义的方法实现也不可调用(甚至通过 super()也不可以)。
使用元类ABCMeta创建的类具有以下方法:
将子类注册为此ABC的“虚拟子类”。例如:
from abc import abstractmethod,ABCMeta
# 1. 虚拟子类 通过register
class MyABC1(metaclass=ABCMeta):
pass
@abstractmethod
def get_name(self):
pass
class Stu1:
pass
def __init__(self,name):
self.name = name
def __str__(self):
return self.name
MyABC1.register(Stu1)
a = Stu1("leng")
print(a,issubclass(Stu1,ABCMeta),issubclass(Stu1,MyABC1),type(MyABC1))
# 输出结果
leng False True <class 'abc.ABCMeta'>
版本3.3中更改:返回已注册的子类,以允许用作类装饰器。
在版本3.4中更改:要检测调用register(),您可以使用 get_cache_token()功能。
译者注:这里的ABC的抽象方法get_name()
都可以不用实现。
可以在抽象基类中覆盖此方法↑(必须定义为类方法。)
检查子类是否被视为此ABC的子类。这意味着您可以进一步自定义issubclass
的行为,而无需调用register()并且要考虑每个ABC子类。(这个类方法是被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(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模块还提供以下装饰器:
指示某方法是抽象方法的装饰器。
使用这个装饰器要求类的元类是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模块还支持以下传统装饰器:
版本3.2中的新功能。
自从3.3版本不推荐使用:现在可以使用classmethod和 abstractmethod,使这个装饰是多余的。
class C(ABC):
@classmethod
@abstractmethod
def my_abstract_classmethod(cls, ...):
...
它是内置classmethod()的子类,表示抽象类方法。类似于abstractmethod()。
版本3.2中的新功能。
自从3.3版本不推荐使用:现在可以使用staticmethod用 abstractmethod,使这个装饰是多余的。
class C(ABC):
@staticmethod
@abstractmethod
def my_abstract_staticmethod(...):
...
内置staticmethod()的子类,表示抽象的staticmethod。否则它类似于abstractmethod()。
自从3.3版本不推荐使用:现在可以使用property,property.getter(), property.setter(),property.deleter()
和abstractmethod()组合,使这个装饰是多余的。
内置property()的子类,表示抽象属性。
class C(ABC):
@property
@abstractmethod
def my_abstract_property(self):
...
上面的示例定义了一个只读属性; 您还可以通过将一个或多个基础方法适当地标记为abstract来定义读写抽象属性:
class C(ABC):
@property
def x(self):
...
@x.setter
@abstractmethod
def x(self, val):
...
如果只有一些组件是抽象的,那么只需要更新那些组件以在子类中创建具体属性:
class D(C):
@C.x.setter
def x(self, val):
...
返回当前的抽象基类缓存标记。
令牌是一个不透明对象(支持相等性测试),用于标识虚拟子类的抽象基类高速缓存的当前版本。令牌随着ABCMeta.register()对任何ABC的每次调用而改变。
版本3.4中的新功能。