详解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中的fabs()方法的使用
May 14 Python
python生成圆形图片的方法
Mar 25 Python
Python创建或生成列表的操作方法
Jun 19 Python
在Pytorch中使用样本权重(sample_weight)的正确方法
Aug 17 Python
浅析PyTorch中nn.Linear的使用
Aug 18 Python
python计算n的阶乘的方法代码
Oct 25 Python
pycharm部署、配置anaconda环境的教程
Mar 24 Python
Selenium自动化测试工具使用方法汇总
Jun 12 Python
浅谈anaconda python 版本对应关系
Oct 07 Python
python GUI计算器的实现
Oct 09 Python
Python3 类型标注支持操作
Jun 02 Python
关于Python OS模块常用文件/目录函数详解
Jul 01 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
PHP新手上路(十二)
2006/10/09 PHP
PHP中操作ini配置文件的方法
2013/04/25 PHP
php提高网站效率的技巧
2015/09/29 PHP
php英文单词统计器
2016/06/23 PHP
swoole锁的机制代码实例讲解
2021/03/04 PHP
基于jquery的让页面控件不可用的实现代码
2010/04/27 Javascript
动态的改变IFrame的高度实现IFrame自动伸展适应高度
2012/12/28 Javascript
当前流行的JavaScript代码风格指南
2014/09/10 Javascript
js关于命名空间的函数实例
2015/02/05 Javascript
jquery实现鼠标滑过显示提示框的方法
2015/02/05 Javascript
javascript实现点击按钮弹出一个可关闭层窗口同时网页背景变灰的方法
2015/05/13 Javascript
JQuery节点元素属性操作方法
2015/06/11 Javascript
jQuery简单实现input文本框内灰色提示文本效果的方法
2015/12/02 Javascript
jQuery mobile类库使用时加载导航历史的方法简介
2015/12/04 Javascript
AngularJS入门教程之迭代器过滤详解
2016/08/18 Javascript
你真的了解BOM中的history对象吗
2017/02/13 Javascript
DataTables添加额外的查询参数和删除columns等无用参数实例
2017/07/04 Javascript
微信小程序自动客服功能
2017/11/02 Javascript
基于ES6作用域和解构赋值详解
2017/11/03 Javascript
玩转Koa之核心原理分析
2018/12/29 Javascript
关于vue利用postcss-pxtorem进行移动端适配的问题
2019/11/20 Javascript
React.js组件实现拖拽排序组件功能过程解析
2020/04/27 Javascript
JavaScript事件委托实现原理及优点进行
2020/08/29 Javascript
Ant Design Pro 之 ProTable使用操作
2020/10/31 Javascript
Python的Bottle框架中返回静态文件和JSON对象的方法
2015/04/30 Python
Python中使用OpenCV库来进行简单的气象学遥感影像计算
2016/02/19 Python
Tornado协程在python2.7如何返回值(实现方法)
2017/06/22 Python
完美解决keras 读取多个hdf5文件进行训练的问题
2020/07/01 Python
装上这 14 个插件后,PyCharm 真的是无敌的存在
2021/01/11 Python
纯CSS实现颜色渐变效果(包含环形渐变、线性渐变、彩虹效果等)
2014/05/07 HTML / CSS
关于解决iframe标签嵌套问题的解决方法
2020/03/04 HTML / CSS
法律专业实习鉴定
2013/12/22 职场文书
工会工作先进事迹
2014/08/18 职场文书
财政专业大学生职业生涯规划书
2014/09/17 职场文书
钱学森电影观后感
2015/06/04 职场文书
使用golang编写一个并发工作队列
2021/05/08 Golang