详解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网络编程学习笔记(二):socket建立网络客户端
Jun 09 Python
python中对list去重的多种方法
Sep 18 Python
Python实现设置windows桌面壁纸代码分享
Mar 28 Python
Python中死锁的形成示例及死锁情况的防止
Jun 14 Python
Python的标准模块包json详解
Mar 13 Python
python调用Delphi写的Dll代码示例
Dec 05 Python
python 接收处理外带的参数方法
Dec 03 Python
python提取照片坐标信息的实例代码
Aug 14 Python
tensorflow 环境变量设置方式
Feb 06 Python
手把手教你安装Windows版本的Tensorflow
Mar 26 Python
PyQt5如何将.ui文件转换为.py文件的实例代码
May 26 Python
Python实现简单猜数字游戏
Feb 03 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
MongoDB在PHP中的常用操作小结
2014/02/20 PHP
php实现XML和数组的相互转化功能示例
2017/02/08 PHP
laravel在中间件内生成参数并且传递到控制器中的2种姿势
2019/10/15 PHP
Extjs学习笔记之六 面版
2010/01/08 Javascript
在Iframe中获取父窗口中表单的值(示例代码)
2013/11/22 Javascript
JS数组(Array)处理函数整理
2014/12/07 Javascript
Javascript常用字符串判断函数代码分享
2014/12/08 Javascript
JavaScript实现的字符串replaceAll函数代码分享
2015/04/02 Javascript
VUEJS实战之修复错误并且美化时间(2)
2016/06/13 Javascript
AngularJS使用指令增强标准表单元素功能
2016/07/01 Javascript
深入理解Angular2 模板语法
2016/08/07 Javascript
webpack常用配置项配置文件介绍
2016/11/07 Javascript
js a标签点击事件
2017/03/30 Javascript
浅谈Angular2 模块懒加载的方法
2017/10/04 Javascript
VUE前端cookie简单操作
2017/10/17 Javascript
JS计算输出100元钱买100只鸡问题的解决方法
2018/01/04 Javascript
React SSR样式及SEO的实践
2018/10/22 Javascript
NodeJs 模仿SIP话机注册的方法
2019/06/21 NodeJs
[03:36]2015国际邀请赛第二日现场精彩集锦
2015/08/06 DOTA
Python自动化测试工具Splinter简介和使用实例
2014/05/13 Python
图文讲解选择排序算法的原理及在Python中的实现
2016/05/04 Python
Python实现获取nginx服务器ip及流量统计信息功能示例
2018/05/18 Python
Python爬虫爬取新浪微博内容示例【基于代理IP】
2018/08/03 Python
用Pycharm实现鼠标滚轮控制字体大小的方法
2019/01/15 Python
python实现PID算法及测试的例子
2019/08/08 Python
What's the difference between Debug and Trace class? (Debug类与Trace类有什么区别)
2013/09/10 面试题
历史学专业大学生找工作的自我评价
2013/10/16 职场文书
自荐书4要点
2014/01/25 职场文书
社区服务活动小结
2014/07/08 职场文书
关于群众路线的心得体会
2014/11/05 职场文书
工程质检员岗位职责
2015/04/08 职场文书
2016年小学生新年寄语
2015/08/18 职场文书
技术转让协议书
2016/03/19 职场文书
开学季:喜迎新生,迎新标语少不了
2019/11/07 职场文书
JAVA API 实用类 String详解
2021/10/05 Java/Android
Redis命令处理过程源码解析
2022/02/12 Redis