深度理解Class类、Object类、Type元类
- 1.Class类、Object类、Type元类的表面关系
- 2.Class、Object、Type解释
- 3.关系详解
- 4.那么如何看待object、type在Python面对对象概念中的一席之地呢?
- 5.那么object、type扮演了什么样的角色呢?他们对class又分别做了什么哪些动作?
- 6.Python在代码层面也提供了一些方法去判断继承和类型
- 总结:从事务看本质
提要:作为普通的Python开发者来讲,深入理解object、type不是必要的,但了解他们确实会有所帮助的。本篇文章的本意是为了元编程打理论基础,但我相信无论是否元编程,本章也将大有裨益。
1.Class类、Object类、Type元类的表面关系
- object:object类是所有类(class)的父类,包括type类,object类的父类为空
- type:type类是所有类的类型,即为所有类(class)都可由type实例化而来,包括type类和父类object。
- class:继承自object,同时,由type进行实例化。其中,type就是我们所讲的元类(metaclass)
print('type的父类是:',type.__base__)
class test:
pass
print('class的父类是:',test.__base__)
print('object的父类是:',object.__base__)
2.Class、Object、Type解释
- class:是典型的面对对象编程的表现形式,为定义对象的属性、行为提供了一个模板。更多的使用场景还是在后台需要个体实例的情况下;一般的model层开发,几乎还是用的函数编程。
- object:Python3中所有class的顶级父类,在class编程中,隐式的自动继承object,object类为其提供了一些内置函数。在我们Python3的开发中,已经不需要去关注object类。
print(list(object.__dict__.keys()))
- type:type是所有对象的顶级类型。在class编程中,隐式的默认声明metaclass=type。
3.关系详解
- class 与 object的关系:
在Python3中,已经隐式的自动继承了object方法,所以我们平时编写的class的的默认内置方法其实是由object方法提供的,Python3中任何一个类都默认继承了Object,是不可改变的。并且每个class的顶级父类都是object。
- class 与 type的关系:
在Python3中,已经隐式的默认了元类是type类,所以每个class的顶级类型都是type类
- object、type:本文的核心之一就是阐述object与type的关系。
上述有一个比较绕的逻辑是object类是type的父类,而object类是type实例化而来,我想了很久怎么去解释和阐述这个逻辑,发现其实从Python的层面来讲已经解释不清了,于是翻到了C的源码,又查了很多资料,翻了很多博客。这里我想结合所有所得总结一些信息:不用去深究object和type的深度关系是什么,除非你想去看完C的PyObject部分,我相信对绝大多数人来讲结果一定是戛然而止的。那么我们只需要知道type的父类是object,object的类型是type便好;也就是我上面说的所有类的顶级类型是type,所有类的顶级父类是object。所以,他们之间与其说是有所关系,不如说是相互协作。下面我会详解他们是如何协作的
4.那么如何看待object、type在Python面对对象概念中的一席之地呢?
这里引用这位博主的一句话。
可以把二者理解为是两个体系的王者,object是站在继承关系顶点的存在,所有的类最后都是继承自object的,object没有父类了,所以这里输出为空(),object是type类型的,也就是说type是站在类型关系顶端的存在,所有的类型都难逃type类型的掌握,所以object和type自己的类型都是type,type的父类是object。
我个人觉得这样的解释也算合理,对普通的开发人员而言已经足够。但如果你想做更加高级的动作,一定要看看下文;皆在我对继承、类型和抽象的理解。
5.那么object、type扮演了什么样的角色呢?他们对class又分别做了什么哪些动作?
object让类有了可以代代相传的通道,可以继承和实例化延续下去,它延续了一个技能,就需要元类,元类里面可以定制类的行为,比如重写__new__或者__init__等方法,就像基因编辑一样,制造出一个按照我们要求工作的娃娃。。
与其说object、type扮演了什么角色,不如讲什么是继承,什么是类型。事实上,Python对此的命名和抽象非常有水平,我想换一种类比去解释这个概念,这里的继承和类型放在中华概念中可以解释为 行为传承和物种生成(通俗点说,就是你向谁学东西,谁创造了你)(在此不得不惊叹guido对Python的审美已经到了极其优雅的程度,让人叹为观止),行为传承让成型的物种有了可以将技能代代相传的通道。那么成型的物种怎么理解呢:既然是物种,那么总有个发育和成型的过程吧。而type就是用来控制这个发育过程的直到它物种成型,然后继承行为,所以从执行顺序上讲object是在type之后。那么type在代码层面又是如何做的呢?我们从下面这个例子看一下:
class MyMeta(type):
def __new__(cls, *args, **kwargs):
print("元类中的__new__被调用")
return super(MyMeta, cls).__new__(cls, *args, **kwargs)
def __init__(self, *args, **kwargs):
print("元类中的__init__被调用")
return super(MyMeta, self).__init__(*args, **kwargs)
def __call__(self, *args, **kwargs):
print("元类中的__call__被调用")
return super(MyMeta, self).__call__(*args, **kwargs)
class MyClass(metaclass=MyMeta):
def __new__(cls, *args, **kwargs):
print("主类中的__new__被调用")
return super(MyClass, cls).__new__(cls, *args, **kwargs)
def __init__(self, *args, **kwargs):
print("主类中的__init__被调用")
return super(MyClass, self).__init__(*args, **kwargs)
def __call__(self, *args, **kwargs):
print("主类中的__call__被调用")
return super(MyClass, self).__call__(*args, **kwargs)
print("-------------------------\n创建具体的对象前\n----------------------------------")
m = MyClass()
元类中的__new__被调用
元类中的__init__被调用
-------------------------
创建具体的对象前
----------------------------------
元类中的__call__被调用
主类中的__new__被调用
主类中的__init__被调用
相信上图已经非常明了。在MyClass代码被加载进内存的时候就已经执行了元类的相关函数,这个过程其实就是物种发育成型。所以在我们平时的普通开发中,几乎都是基于物种发育成型后赋予相关技能行为的代码编写。而这种控制物种发育的过程,在Python代码层面叫做元编程。
执行顺序:
元类:new、init、call、 子类new、init、call。注意每个函数的第一个参数都是由上一个函数执行的返回结果,new的第一个参数是外部返回的结果,声明cls,其他函数的第一个参数是内部函数返回的结果声明为self,这种命名是guido做的一种范式,这样的命名设计是合理的。
6.Python在代码层面也提供了一些方法去判断继承和类型
提供了内置函数isinstance去比较类型关系;
提供了内置函数issubclass去比较继承关系;
提供了type()去判断类型;
提供了对象的__base__()方法去获取上一级的父类
总结:从事务看本质
object和type的产生本身就是一种自然抽象。其实从汇编、C、面向过程、面向对象这一路演化都是为了抽象,包括造轮子,语言生态,其实都是抽象的具体表现。那么殊途同归,从硬件到软件一路走来,所有动作都是抽象,它不是创造,而是优化,但某些优化趋近于创造。软件设计思想,其实都是从自然、人文等关系演化类比而来。软件思想就是一方世界,是自然的产生,是真实世界的映射,它和我们一样,在进步、在进化,与我们并肩前行。