Python 模块:abc

模块abc提供了在 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

class abc.ABCMeta

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

使用该元类以创建抽象基类。抽象基类可以像 mix-in 类一样直接被子类继承。你也可以将不相关的具体类(包括内建类)和抽象基类注册为“抽象子类” —— 这些类以及它们的子类会被内建函数 issubclass() 识别为对应的抽象基类的子类,但是该抽象基类不会出现在其 MRO(Method Resolution Order,方法解析顺序)中,抽象基类中实现的方法也不可调用(即使通过 super() 调用也不行)。

使用 ABCMeta 作为元类创建的类含有如下方法:

register(subclass)

将“子类”注册为该抽象基类的“抽象子类”,例如:

from abc import ABC


class MyABC(ABC):
    pass


# noinspection PyUnresolvedReferences
MyABC.register(tuple)
assert issubclass(tuple, MyABC)
assert isinstance((), MyABC)

你也可以在虚基类中重载这个方法。

__subclasshook__(subclass)

(必须定义为类方法。)

检查 subclass 是否是该抽象基类的子类。也就是说对于那些你希望定义为该抽象基类的子类的类,你不用对每个类都调用 register() 方法了,而是可以直接自定义 issubclass 的行为。(这个类方法是在抽象基类的 __subclasscheck__() 方法中调用的。)

该方法必须返回 True, False 或是 NotImplemented。如果返回 True,subclass 就会被认为是这个抽象基类的子类。如果返回 False,无论正常情况是否应该认为是其子类,统一视为不是。如果返回 NotImplemented,子类检查会按照正常机制继续执行。

为了对这些概念做一演示,请看以下定义 ABC 的示例:

from abc import ABC, abstractmethod


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


# noinspection PyUnresolvedReferences
MyIterable.register(Foo)

ABC MyIterable 定义了标准的迭代方法 __iter__() 作为一个抽象方法。这里给出的实现仍可在子类中被调用。get_iterator() 方法也是 MyIterable 抽象基类的一部分,但它并非必须被非抽象派生类所重载。

在这里定义的__subclasshook__()类方法表示,在其__dict__中有__iter__()方法的任何类(或其基类之一的,通过__mro__列表访问)也被认为是一个 MyIterable 对象。

最后,最后一行使 Foo 称为 MyIterable 的虚子类,尽管它没有定义一个__iter__()方法(它使用了旧式可迭代协议,定义了__len__()方法和__getitem__()方法)。注意,这不会使 get_iterator 作为 Foo 的实例方法可用,因此它是单独提供的。

此外,abc 模块还提供了装饰器:

@abc.abstractmethod

用于声明抽象方法的装饰器。

使用此装饰器要求类的元类是 ABCMeta 或是从该类派生。一个具有派生自 ABCMeta 的元类的类不可以被实例化,除非它全部的抽象方法和特征属性均已被重载。抽象方法可通过任何普通的“super”调用机制来调用。abstractmethod() 可被用于声明特性属性和描述器的抽象方法。

不支持向类动态添加抽象方法,也不支持在创建方法或类时尝试修改其抽象状态。abstractmethod()只影响使用正规继承派生的子类,注册在 ABC's register()方法的虚子类不受影响

When abstractmethod() is applied in combination with other method descriptors, it should be applied as the innermost decorator, as shown in the following usage examples:

当 abstractmethod()与其他方法描述符结合应用时,它应该作为最内部的装饰符应用,如下面的用法示例所示:

from abc import ABC, 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__将自己标识为抽象。一般来说,如果组成描述符的任何方法都是抽象的,则该属性应该是真,Python 的内置属性相当于下面这个示例:

class Descriptor:
    def _fget(self):
        pass
    
    def _fset(self):
        pass
    
    def _fdel(self):
        pass
    
    @property
    def __isabstractmethod__(self):
        return any(getattr(f, '__isabstractmethod__', False) for f in (self._fget, self._fset, self._fdel))

注解:与 Java 抽象方法不同,这些抽象方法可能有一个实现。这个实现可以通过 super()机制从重写它的类调用。这可以作为在使用协作的多重继承框架中的超类调用的终点。

abc 模块还提供了这些函数:

abc.get_cache_token()

返回当前抽象基类的缓存令牌

令牌是一个不透明对象(它支持等式测试),用于标识虚拟子类的抽象基类缓存的当前副本,每次调用 ABCMeta.register()时,任何在 ABC 上的令牌都会发生变化。

本文分享自微信公众号 - 小陈学Python(gh_a29b1ed16571)

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2019-04-20

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏呆呆熊的技术路

mysql 关于时间类型的刨坑之路

前两天有做一个基于binglog的数据库实时同步,一张老数据表里有DATETIME、TIMESTAMP不同的时间字段类型,看起来值都是一样的,并且默认值都设置的...

19030
来自专栏服务器安全专线

如何解决 Linux 实例 pip 操作时的超时问题

  pip 是当前最流行的 Python 安装包管理工具之一,很多阿里云用户会通过 pip 更新系统源。阿里云的 pip 源地址有以下三处:

12900
来自专栏机器视觉CV

Beautiful Soup与运用(猫眼电影榜单)

Beautiful Soup是Python的一个HTML/XML的解析库,可以用来获取网页信息 输入文档为Unicode 编码,输出文档为UTF-8编码,不需考...

11720
来自专栏AI科技大本营的专栏

又一AI神器!3行代码5秒抠图,根本无需PS

是不是很赞?什么 PS 、PPT 修图都 low 爆了,你软件还没打开,我这边都修完了。

27460
来自专栏AI科技大本营的专栏

30分钟看懂XGBoost的基本原理

xgboost是一种集成学习算法,属于3类常用的集成方法(bagging,boosting,stacking)中的boosting算法类别。它是一个加法模型,基...

11820
来自专栏AI科技大本营的专栏

喜大普奔!GitHub官方文档推出中文版

最近程序员交友圈出了一个大新闻,GitHub 帮助文档正式推出中文版了,之前一直都是只有英文文档,看起来费劲不方便。

8720
来自专栏机器视觉CV

机器学习 | 猫狗大战

对于机器学习来说,数据的重要性无可厚非,大部分处理机器学习的问题都是在处理数据,包括数据的清洗,归一化等,好的数据质量能大大提高模型的预测性能

18220
来自专栏跟Qt君学编程

一些Qt第三方语言绑定库(修正)

26620
来自专栏爱写Bug

LeetCode 141:环形链表 Linked List Cycle

为了表示给定链表中的环,我们使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。 如果 pos 是 -1,则在该链表中没有环。

8470
来自专栏程序猿杂货铺

分析了16年的福利彩票记录,原来可以用Python这么买彩票

上周被一则新闻震惊到了,《2454万元大奖无人认领!福彩史上第二大弃奖在广东中山产生 》,在2019年5月2日开奖的双色球中,广东中山一位彩民博中2454万元,...

14420

扫码关注云+社区

领取腾讯云代金券

年度创作总结 领取年终奖励