详解Python是如何实现issubclass的


Posted in Python onJuly 24, 2019

使用Python内置的issubclass方法很方便的检测一个类是否是另一个类的子类。

这个是issubclass的文档:

issubclass(class, classinfo)

Return true if class is a subclass (direct, indirect or virtual) of classinfo. A class is considered a subclass of itself. classinfo may be a tuple of class objects, in which case every entry in classinfo will be checked. In any other case, a TypeError exception is raised.

一个类的子类可以是直接的、间接的、或者是虚拟的。

issubclass的第二个参数classinfo可以是一个类对象或者包含类对象的tuple(只要其中一个检测成功即返回True)。

一些使用示例:

>>> class A(object):
...   pass
...
>>> class B(A):
...   pass
...
>>> class C(B, A):
...   pass
...
>>> class D(C):
...   pass
...
>>> issubclass(D, D), issubclass(D, C), issubclass(D, B), issubclass(D, A), issubclass(D, object)
(True, True, True, True, True)
>>> D.__bases__
(<class '__main__.C'>,)
>>> D.__mro__
(<class '__main__.D'>, <class '__main__.C'>, <class '__main__.B'>, <class '__main__.A'>, <class 'object'>)

D是D的子类,D定义时的基类是C,所以D是C的子类,而且D是B,A,object的间接子类。

__mro__是类属性, 在类定义完毕Python解析器便通过一种C3算法将所有的父类以method resolution order的顺序保存到一个元组里, 成为类的属性。

所以issubclass可以这样简单的实现:

def issubclass(cls, classinfo):
  if classinfo in cls.__mro__:
    return True
  return False

Python的issubclass是内置函数(一般是C实现),实际上要复杂很多,要检测参数类型,如第一个参数必须是type类型,第二个参数是type类型或者tuple类型。还要考虑该类是否是虚拟的子类,以及子类的子类。

例如:

>>> from collections import abc
>>> class E:
...   def __len__(self):
...     return 1
...
>>> issubclass(E, abc.Sized)
True
>>> E.__mro__
(<class '__main__.E'>, <class 'object'>)
>>> class F:
...   pass
...
>>> issubclass(F, abc.Sized)
False
>>> abc.Sized.register(F)
<class '__main__.F'>
>>> issubclass(F, abc.Sized)
True

Python是动态类型语言,长久以来使用Duck type(鸭子类型)形式编程,不管对象是什么类型,只要实现了所需要的方法。

现在有了ABCs, 可以用于判断某个类或者某个对象是不是ABCs的子类或者实例,但这个类并不需要显示的继承于ABCs, 因为python内置的ABCs有一种注册机制可将一个类注册为它的子类。如上例子的register方法。

还有一种机制是可以定制一个__subclasshook__方法,将某种类型的类认定为子类。

如abc.Sized的__subclasshook__是这样子的:

@classmethod
def __subclasshook__(cls, C):
  if cls is Sized:
    if any("__len__" in B.__dict__ for B in C.__mro__):
      return True
  return NotImplemented

所以有__len__方法的E类是abc.Sized的子类, 这个__subclasshook__方法是通过__subclasscheck__方法调用的,这个__subclasscheck__是每一个ABC类都有的方法,在ABCMeta类(其他ABC类都继承于它)实现。

现在的issubclass函数的实现,会先判断classinfo是否有__subclasscheck__方法,如果有此方法,则判断子类的逻辑由该方法返回,即覆盖issubclass的实现(CPython)。

__subclasscheck__会分几个步骤进行判断:

  1. 调用__subclasshook__方法,如果有方法定义
  2. 检查自己是否在待检测类的__mro__列表里
  3. 递归检查待检测类是否是在注册子类(内置_abc_registry列表属性)
  4. 递归检查待检测类是否是自己子类的子类

具体源码在: https://github.com/python/cpython/blob/3.6/Lib/abc.py#L194-L231

相关的CPython实现在: https://github.com/python/cpython/blob/0ccc0f6c7495be9043300e22d8f38e6d65e8884f/Objects/abstract.c#L2223

而基本上isinstance(object, classinfo)方法的实现只需要调用issubclass(type(object), classinfo)

参考:

29.7. abc — Abstract Base Classes : https://docs.python.org/3/library/abc.html
PEP 3119 ? Introducing Abstract Base Classes: https://www.python.org/dev/peps/pep-3119/

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Python 相关文章推荐
python用ConfigObj读写配置文件的实现代码
Mar 04 Python
Python聚类算法之DBSACN实例分析
Nov 20 Python
在python中使用正则表达式查找可嵌套字符串组
Oct 24 Python
详解Python里使用正则表达式的ASCII模式
Nov 02 Python
python进行两个表格对比的方法
Jun 27 Python
Django项目后台不挂断运行的方法
Aug 31 Python
python读取ini配置的类封装代码实例
Jan 08 Python
Python基于内置库pytesseract实现图片验证码识别功能
Feb 24 Python
Python数据库封装实现代码示例解析
Sep 05 Python
Python使用内置函数setattr设置对象的属性值
Oct 16 Python
Jupyter Notebook 安装配置与使用详解
Jan 06 Python
python 如何执行控制台命令与操作剪切板
May 20 Python
Django中在xadmin中集成DjangoUeditor过程详解
Jul 24 #Python
Django 权限认证(根据不同的用户,设置不同的显示和访问权限)
Jul 24 #Python
Django 创建/删除用户的示例代码
Jul 24 #Python
python3.6+django2.0+mysql搭建网站过程详解
Jul 24 #Python
简单了解python 邮件模块的使用方法
Jul 24 #Python
python 根据字典的键值进行排序的方法
Jul 24 #Python
如何使用Flask-Migrate拓展数据库表结构
Jul 24 #Python
You might like
基于文本的访客签到簿
2006/10/09 PHP
php计算数组不为空元素个数的方法
2014/01/27 PHP
从零开始学YII2框架(二)通过 Composer 安装扩展插件
2014/08/20 PHP
php中base_convert()进制数字转换函数实例
2014/11/20 PHP
PHP利用APC模块实现大文件上传进度条的方法
2015/10/29 PHP
XHTML-Strict 内允许出现的标签
2006/12/11 Javascript
Javascript 判断 object 的特定类转载
2007/02/01 Javascript
SWFObject Flash js调用类
2008/07/08 Javascript
javascript/jquery获取地址栏url参数的方法
2014/03/05 Javascript
Node.js中安全调用系统命令的方法(避免注入安全漏洞)
2014/12/05 Javascript
JavaScript编程中的Promise使用大全
2015/07/28 Javascript
基于JavaScript怎么实现让歌词滚动播放
2015/11/03 Javascript
JS获取文件大小方法小结
2015/12/08 Javascript
BootStrap Fileinput的使用教程
2016/12/30 Javascript
AngularJS2中一种button切换效果的实现方法(二)
2017/03/27 Javascript
AngularJS实现的输入框字数限制提醒功能示例
2017/10/26 Javascript
JavaScript中的垃圾回收与内存泄漏示例详解
2019/05/02 Javascript
JavaScript实现随机点名器实例详解
2019/05/07 Javascript
小程序实现上下切换位置
2020/11/16 Javascript
详解Python中的多线程编程
2015/04/09 Python
浅谈python中的getattr函数 hasattr函数
2016/06/14 Python
windows下ipython的安装与使用详解
2016/10/20 Python
Python基于FTP模块实现ftp文件上传操作示例
2018/04/23 Python
Django框架中序列化和反序列化的例子
2019/08/06 Python
Pycharm 字体大小调整设置的方法实现
2019/09/27 Python
浅谈ROC曲线的最佳阈值如何选取
2020/02/28 Python
CSS3+js实现简单的时钟特效
2015/03/18 HTML / CSS
使用CSS3编写灰阶滤镜来制作黑白照片效果的方法
2016/05/09 HTML / CSS
HTML5中的强制下载属性download使用实例解析
2016/05/12 HTML / CSS
html5.2 dialog简介详解
2018/02/27 HTML / CSS
英国在线珠宝店:The Jewel Hut
2017/03/20 全球购物
Kathmandu新西兰官网:新西兰户外运动品牌
2019/07/27 全球购物
如何将无状态会话Bean发布为WEB服务,只有无状态会话Bean可以发布为WEB服务?
2015/12/03 面试题
四风专项整治工作情况汇报
2014/10/28 职场文书
软件项目经理岗位职责
2015/04/01 职场文书
Springboot集成kafka高级应用实战分享
2022/08/14 Java/Android