Python 3.7.1 模块 abc 抽象基类

19 篇文章 1 订阅
11 篇文章 0 订阅

源码: Lib/abc.py


1. 前言

该模块提供了用于在Python中定义抽象基类(ABC)的基础结构,如 PEP 3119 中所述; 请参阅PEP,了解为何将其添加到Python中。(也可以看看PEP 3141numbers模块关于基于ABC的数字类型层次结构的 。)

collections模块有一些来自ABCs的具体类; 当然,这些可以进一步推导出来。此外, collections.abc子模块还有一些ABCs可用于测试类或实例是否提供特定接口,例如,如果是否可哈希,或者它是否是一个映射。

2. 内容

该模块提供了 元类 ABCMeta 用于定义ABCs 和一个辅助类ABC,以通过继承来定义ABCs:

2.1 类

class abc.ABC

一个辅助类,具有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)) # <class 'abc.ABCMeta'>
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

class abc.ABCMeta

用于定义抽象基类(ABCs)的元类。

通过传递metaclass=ABCMeta来定义抽象基类,例如:

from abc import ABCMeta

class MyABC(metaclass=ABCMeta):
    pass

版本3.4中的新功能。

使用此元类创建ABC。ABC可以直接子类化,然后充当混合类。您还可以将不相关的具体类(甚至是内置类)和不相关的ABC注册为“虚拟子类” - 这些将被内置函数issubclass() 视为ABC注册的子类,但注册ABC将不会显示在他们的MRO(方法解析顺序)中,注册ABC定义的方法实现也不可调用(甚至通过 super()也不可以)。

2.2 注册虚拟子类方法

使用元类ABCMeta创建的类具有以下方法:

register(subclass)

将子类注册为此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()都可以不用实现。

__subclasshook__(subclass)

可以在抽象基类中覆盖此方法↑(必须定义为类方法。)

检查子类是否被视为此ABC的子类。这意味着您可以进一步自定义issubclass的行为,而无需调用register()并且要考虑每个ABC子类。(这个类方法是被ABC中的__subclasscheck__()调用的。)

这个方法应该返回TrueFalse或者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中不可见,因此它是单独提供的。

2.3 装饰器(其实也是方法)

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,使这个装饰是多余的。

class C(ABC):
    @classmethod
    @abstractmethod
    def my_abstract_classmethod(cls, ...):
        ...

它是内置classmethod()的子类,表示抽象类方法。类似于abstractmethod()。

@abc.abstractstaticmethod

版本3.2中的新功能。

自从3.3版本不推荐使用:现在可以使用staticmethod用 abstractmethod,使这个装饰是多余的。

class C(ABC):
    @staticmethod
    @abstractmethod
    def my_abstract_staticmethod(...):
        ...

内置staticmethod()的子类,表示抽象的staticmethod。否则它类似于abstractmethod()。

@abc.abstractproperty

自从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):
        ...

2.4 其他方法

abc.get_cache_token()

返回当前的抽象基类缓存标记。

令牌是一个不透明对象(支持相等性测试),用于标识虚拟子类的抽象基类高速缓存的当前版本。令牌随着ABCMeta.register()对任何ABC的每次调用而改变。

版本3.4中的新功能。

  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值