详解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 相关文章推荐
Python3基础之基本数据类型概述
Aug 13 Python
Python中模块pymysql查询结果后如何获取字段列表
Jun 05 Python
Django自定义认证方式用法示例
Jun 23 Python
python 实现上传图片并预览的3种方法(推荐)
Jul 14 Python
Django教程笔记之中间件middleware详解
Aug 01 Python
详解python3中的真值测试
Aug 13 Python
python读取TXT每行,并存到LIST中的方法
Oct 26 Python
Python实现的特征提取操作示例
Dec 03 Python
对python 多个分隔符split 的实例详解
Dec 20 Python
python内打印变量之%和f的实例
Feb 19 Python
Python实现常见的几种加密算法(MD5,SHA-1,HMAC,DES/AES,RSA和ECC)
May 09 Python
FP-growth算法发现频繁项集——构建FP树
Jun 24 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
MySql中正则表达式的使用方法描述
2008/07/30 PHP
php继承的一个应用
2011/09/06 PHP
解决文件名解压后乱码的问题 将文件名进行转码的代码
2012/01/10 PHP
PHP exif扩展方法开启详解
2014/07/28 PHP
PHP获取文件相对路径的方法
2015/02/26 PHP
php三种实现多线程类似的方法
2015/10/30 PHP
ThinkPHP框架分布式数据库连接方法详解
2017/03/14 PHP
PHP数字金额转换成中文大写显示
2019/01/05 PHP
Aptana调试javascript图解教程
2009/11/30 Javascript
JS对外部文件的加载及对IFRMAME的加载的实现,当加载完成后,指定指向方法(方法回调)
2011/07/04 Javascript
js 显示base64编码的二进制流网页图片
2014/04/04 Javascript
Node.js插件的正确编写方式
2014/08/03 Javascript
Javascript基础教程之变量
2015/01/18 Javascript
js表单处理中单选、多选、选择框值的获取及表单的序列化
2016/03/08 Javascript
Javascript的比较汇总
2016/07/25 Javascript
Node.js中用D3.js的方法示例
2017/01/16 Javascript
Map.vue基于百度地图组件重构笔记分享
2017/04/17 Javascript
浅谈vue.js导入css库(elementUi)的方法
2018/03/09 Javascript
在layer弹层layer.prompt中,修改placeholder的实现方法
2019/09/27 Javascript
python使用PyQt5的简单方法
2019/02/27 Python
centos+nginx+uwsgi+Django实现IP+port访问服务器
2019/11/15 Python
CSS3 Media Queries(响应式布局可以让你定制不同的分辨率和设备)
2013/06/06 HTML / CSS
Currentbody美国/加拿大:美容仪专家
2020/03/09 全球购物
高二美术教学反思
2014/01/14 职场文书
火锅店营销方案
2014/02/26 职场文书
安全生产管理责任书
2014/04/16 职场文书
小学一年级学生评语
2014/04/22 职场文书
酒店优秀员工事迹材料
2014/06/02 职场文书
经理任命书模板
2014/06/06 职场文书
经济管理专业求职信
2014/06/09 职场文书
政府四风问题整改措施
2014/10/04 职场文书
运动会宣传语
2015/07/13 职场文书
大学副班长竞选稿
2015/11/21 职场文书
解决MySQL Varchar 类型尾部空格的问题
2022/04/06 MySQL
解决vue-router的beforeRouteUpdate不能触发
2022/04/14 Vue.js
Go语言编译原理之变量捕获
2022/08/05 Golang