当前位置: 首页 > news >正文

36、Python之面向对象:容器类协议与collections.abc

引言

在上一篇文章中,我们通过定义类继承自MutableSequence实现了自定义容器类的效果,使得我们自定义的类,可以进行[]索引、切片,可以进行for循环遍历,可以使用len()函数计算元素个数等,其行为表现得就像是一个内置类一样。

文章的最后,简单介绍了相关魔术方法所表达的容器对象需要统一遵循的协议。

其实,MutableSequence所在的collections.abc模块中定义了一系列的抽象基类。这些类的设计描述了各种容器(比如:列表、集合、字典等)上的编程接口,也就是定义某种特定容器类需要遵循的“协议”。

这些抽象类可以有两个作用:

1、可以将这些抽象类作为基类,用于自定义容器类,这些类要实例化对象,需要实现这些抽象方法(协议),以达成模仿内置容器类型的效果,比如上一篇文章中的Team类。

2、可以作为类型检查,比如判断某个对象是否类似于一个序列、字典等。

今天我们将展开来看下collections.abc中的容器相关的抽象类,梳理相关抽象类的继承关系,从而更好地理解容器类协议。

容器类继承层次图

首先看下collections.abc中容器类相关的抽象类的继承关系图,先对这些有个全局性的掌握。

38f8cecdd9734d101e0477002be6b211.jpeg

需要说明的是,并没有把所有的容器抽象类都列举出来,只是把比较重要在图里呈现了出来。

从图中可以看到,最顶层有三个抽象基类:Sized、Container和Iterable,Collection继承自这三个抽象基类。

Set、Mapping、Sequence都继承自Collection,这三个是三种不可变的容器类型的抽象类。它们又各自有一个Mutable的子类,分别是对应的可变容器类。

容器类协议

首先是基础的单一协议的抽象类:

1、Container:

所有容器的基类,定义了抽象方法__contains__(),用于实现in运算符。

2、Iterable:

支持迭代协议的对象的基类,定义了抽象方法__iter__()。

3、Iterator:

迭代器对象的基类,定义了抽象方法next(),继承自Iterable,提供了一个不执行任何操作的默认__iter__()方法的实现。

4、Sized:

可以确定元素个数的容器的基类,定义了抽象方法__len__()。

5、Hashable:

可用作散列表键的对象的基类,定义了抽象方法__hash__()。

其次是复合的多协议的抽象类:

6、Sequence:

类似于序列的对象的基类,继承自Container、Iterable和Sized,添加了抽象方法__getitem__()和__len__()。同时提供了__contains__()、__iter__()、__reversed__()、index()和count()的默认实现,这些方法是使用__getitem__()和__len__()方法实现的。

7、MutableSequence:

可变序列的基类,继承自Sequence,添加了抽象方法__setitem__()和__delitem__()。同时提供了append()、reverse()、extend()、pop()、remove()和__iadd__()的默认实现。所以,我们在上一篇文章中,定义Team继承MutableSequence时,可以调用append()方法等。

8、Set:

类似于集合的对象的基类,继承自Containter、Iterable和Sized,定义了抽象方法__len__()、__iter__()和__contains__()。同时还提供了__le__()、__lt__()、__eq__()、__ne__()、__gt__()、__ge__()、__and__()、__or__()、__xor__()、__sub__()和isdisjoint()的默认实现。

9、MutableSet:

可变集合的基类,继承自Set,同时添加了抽象方法add()和discard()。同时还提供了clear()、pop()、remove()、__ior__()、__iand__()、__ixor__()和__isub__()的默认实现。

10、Mapping:

支持映射(字典)查找的对象的基类,继承自Container、Iterable和Sized,定义了抽象方法__getitem__()、__len__()和__iter__()。同时提供了__contains__()、keys()、items()、values()、get()、__eq__()和__ne__()的默认实现。

11、MutableMapping:

可变映射(字典)查找对象的基类,继承自Mapping,添加了抽象方法__setitem__()和__delitem__()。同时还添加了pop()、popitem()、clear()、update()和setdefault()的默认实现。

基于上面对这些容器类协议的说明可知,当我们需要自定义容器类或者自定义的类需要模仿内置容器类型的某些行为时,只需要定义类继承需要模仿的协议对应的抽象类。比如,如果只需要支持in操作符,则只需要继承自Container,并实现__contains__()方法即可。

需要说明的是,当我们后面的文章中介绍到“鸭子类型”和魔术方法时,会看到,不继承自这些抽象基类也是可以模仿内置容器类型的行为的,以后再行展开。

关于内置容器类型

最后,再简单看一下内置类型,你觉得已经对这些内置类型再熟悉不过,但也许只是“灯下黑”。

虽然我们已经能够很熟练地使用Python中的内置容器类型,比如:list、tuple、set、dict等。但是,我们通过IDE,比如PyCharm只能看到一个简要的说明,并没有具体的实现,如图所示:

4a59a41e79cd29434acfc8cc19968218.jpeg

从说明中,可以看到list是一个内置的MutableSequence。

但是,如果我们运行下面代码,会觉得有些奇怪:

from collections.abc import MutableSequencel1 = [1, 2, 3]
print(list.__bases__)
print(isinstance(l1, MutableSequence))
print(issubclass(list, MutableSequence))

执行结果:

9d936bd1e86e047619bcb42cd37342db.jpeg

list的基类只有object,并没有MutableSequence。可是,当使用isinstance()进行实例对象判断,以及使用issubclass()进行子类判断时,返回的却是True,似乎有些矛盾。

我们都知道,这些内置类实际上是由C语言实现的,并作为Python解释器的一部分进行编译和优化,一个C语言实现的list类型,是怎么跟Python中的MutableSequence抽象基类产生联系的呢?

如果仔细查看collections.abc的源代码,不难发现:

2f734a31c88242d19df3850a3cee3eaa.jpeg

在MutableSequence类定义之后,调用了register()方法,该方法可用于动态注册一个类,表明这个类是该抽象基类的虚拟子类,即便这个类没有显式地继承该抽象基类。

所以,即便list类的基类只有object,当我们使用isinstance()和issubclass()时,会返回True。

关于抽象基类的register()方法,后面有机会的话,跟元类的概念放在一起进行介绍。

总结

本文梳理了collections.abc模块中的容器抽象类继承关系,展示了继承关系图。并对其中的比较关键的抽象基类所承载的容器类协议作了逐一说明。然后简单验证了内置容器类list与MutableSequence之间的关系。

感谢您的拨冗阅读,如果本文的内容对您学习Python有所帮助,欢迎点赞、收藏。

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • Android进阶之路 - app后台切回前台触发超时保护退出登录
  • Java Web —— 第四天(HTTP协议,Tomcat)
  • 关于RCE
  • 白骑士的Matlab教学附加篇 5.2 代码规范与最佳实践
  • vue.config.js 配置多入口文件
  • LVS负载均衡集群部署之—NAT模式的介绍及搭建步骤
  • DBAPI如何用SQL查询出类似嵌套JSON的树状结构数据(例如省市区父子结构数据)
  • 开源力量,智领云KDP为大数据处理领域注入云原生活力
  • [C++内存管理]new,delete,operator new,opreator delete
  • 生成随机字符串(字母+数字)-批发行业进销存- PHP源码CyberWinApp-SAAS 本地化及未来之窗行业应用跨平台架构
  • Redis7.0.15 主从复制、哨兵模式搭建
  • Topsis法模型(评价类问题)
  • Sql Server索引的创建及优化
  • 多模态:Seed-story故事生成
  • 七、ESP32-S3上使用MicroPython点亮WS2812智能LED灯珠并通过web控制和JS颜色选择器改变灯珠颜色
  • [分享]iOS开发 - 实现UITableView Plain SectionView和table不停留一起滑动
  • 【RocksDB】TransactionDB源码分析
  • 【笔记】你不知道的JS读书笔记——Promise
  • Android交互
  • chrome扩展demo1-小时钟
  • Debian下无root权限使用Python访问Oracle
  • dva中组件的懒加载
  • ECS应用管理最佳实践
  • input的行数自动增减
  • iOS编译提示和导航提示
  • Js基础知识(四) - js运行原理与机制
  • spring-boot List转Page
  • Tornado学习笔记(1)
  • 复习Javascript专题(四):js中的深浅拷贝
  • 诡异!React stopPropagation失灵
  • 理解 C# 泛型接口中的协变与逆变(抗变)
  • 罗辑思维在全链路压测方面的实践和工作笔记
  • 使用Gradle第一次构建Java程序
  • 使用Swoole加速Laravel(正式环境中)
  • 数据库写操作弃用“SELECT ... FOR UPDATE”解决方案
  • 微信小程序上拉加载:onReachBottom详解+设置触发距离
  • 无服务器化是企业 IT 架构的未来吗?
  • 详解NodeJs流之一
  • 转载:[译] 内容加速黑科技趣谈
  • 看到一个关于网页设计的文章分享过来!大家看看!
  • “十年磨一剑”--有赞的HBase平台实践和应用之路 ...
  • 400多位云计算专家和开发者,加入了同一个组织 ...
  • elasticsearch-head插件安装
  • ​插件化DPI在商用WIFI中的价值
  • #define用法
  • #Linux(Source Insight安装及工程建立)
  • $(selector).each()和$.each()的区别
  • (ibm)Java 语言的 XPath API
  • (php伪随机数生成)[GWCTF 2019]枯燥的抽奖
  • (Windows环境)FFMPEG编译,包含编译x264以及x265
  • (动手学习深度学习)第13章 计算机视觉---微调
  • (读书笔记)Javascript高级程序设计---ECMAScript基础
  • (二)十分简易快速 自己训练样本 opencv级联lbp分类器 车牌识别
  • (论文阅读22/100)Learning a Deep Compact Image Representation for Visual Tracking
  • (强烈推荐)移动端音视频从零到上手(上)