在前面,我们介绍了Python中一切皆为对象的理念,并在系列文章中反复提及这个理念。感兴趣的同学可以阅读:Python番外篇:万法归一,一切皆对象 ,这篇文章。
紧接着,我们通过了对Python的标准类型层次结构的梳理,印证了Python中包括类、函数等都是对象,不记得的可以回看:Python番外篇:标准类型层次结构 ,这篇文章。
当时,我们在这篇文章中,简单提及了几个有点奇怪的结论:
1、Python中所有的类对象,都是通过type类实例化的;
2、Python中所有的类的最顶层的父类都是object;
3、object这个类对象是由type类实例化的;
4、type这个类对象是由type类自己实例化的。
当时只是一带而过,也许不少刚接触Python的同学,会有些困惑。由于当时还没有涉及面向对象的部分,就没有展开。
相信通过之前面向对象内容的介绍,以及本文关于元类(metaclass)概念的补充,将对这些结论有一些更加深入的理解。
需要提前说明的是,由于篇幅的限制,本文主要进一步阐释Python中一切皆对象的概念,通过元类的概念,向上不断溯源,探究实例对象通过类来创建,那么类对象又是如何被创建的。
所以,本文所介绍的更多是原理类的“无用”之学,关于元类的实际应用场景的“有用”之学,将在下一篇文章中进行展开介绍。
“元”的概念与“元类”虽然“元”的概念稍显晦涩,但是,稍微类比一下是不难理解的。
比如,思考,有的人的思考方式,是遇到一个问题,仔细分析所有已知条件和约束,然后通过逻辑推理,一步步缜密地推导出问题的解;有的人则是“羚羊挂角,无迹可寻”,一会儿东,一会儿西地大胆假设,最终也能得出问题的解。
相对于“思考”的对象是问题,所谓的“元思考”思考的对象是思考本身, 是反观内省是如何进行思考本身的。所以,元思考,就是思考的思考。
比如,相较于“学习”的对象是知识、技能,“元学习”的对象是学习本身,元学习是学习如何学习的含义,是学习的学习。
那么,相同的逻辑,我们来理解“类”与“元类”的概念。
“类”实例化的对象是类模板所创造出来的实例化对象,“元类”作用的对象不再是实例化对象,而是类对象,是创建类的类。
一切皆对象,类也是对象,被称为类对象(一定要明确区分类对象与实例对象)。所以,元类就是创建类对象的类,元类就是类的类,这里的“类”,如同学习、思考,是动词。
这个概念有点绕,笔者的表达能力又不太靠谱,所以,稍微理解一下,实在不理解,自行搜索引擎吧。
再看type的用法之前的文章中已经提到过了,type是一个类,而不是内置函数,我们来再次看一下type类的定义:
其实,从type的定义描述中,可以看到,我们可以有两种方式获取一个类:
1、type(object):通过一个现有的对象,获取这个对象所属的类。
2、type(name, bases, dict, **kwargs):基于各个参数凭空创建一个新的类。
接下来,我们通过代码演示一下type的使用:
执行结果:
通过代码及执行结果,我们可以有如下结论:
1、基于一个现有的实例对象,通过type可以获取该实例的所属类对象,进而通过()调用的方式创建一个新的对象。所以,从结果的层面看,我们基于一个实例对象,创建出了一个同类的实例对象。
2、创建类对象的方式有两种,一种是显式地通过class关键字进行类的定义,另一种就是通过type()基于各种参数凭空进行一个类对象的创建。
type是一切元类的基类前面我们通过type的第二种用法创建了一个全新的类对象,后续都可以基于这个类对象进行实例对象的创建。所以,可以看出type是在创建类,是类的类,所以type是一个元类。
其实,在Python中有一个关键字metaclass,这个在collections.abc中曾经看到过:
collections.abc中最基础的几个抽象基类:Sized、Container的定义中,均通过metaclass关键字指明了用于构建类对象的元类为ABCMeta。
而从ABCMeta的定义中可以看出,ABCMeta继承自type。
之所以说type是一切元类的基类,是因为:
1、我们在定义类时,不指定创建类对象的元类时,默认的元类都是type。
2、当我们想要自定义一个元类时,通常需要继承自type或者其子类。
关于第一点,我们可以通过type(类名)来看到,以实际代码来看:
执行结果:
我们可以尝试定义一个元类,直接看代码:
执行结果:
从代码及执行结果,可以看出:
1、自定义元类的方法是:自定义一个类继承自type,并实现__new__方法,一般实现的__new__方法中,要调用父类(也就是type)的__new__方法,用于进行类对象的创建。
2、类对象的创建,是在类定义时发生的,即便没有使用该类创建实例对象,也会首先进行类对象的创建。类定义时会自动调用所属元类的__new__方法。
3、指定元类进行类的定义时,type(类名)就不再返回type了,而是对应的元类。
4、自定义的元类本身与type具有二重关系,其一,继承自type,所以该元类的父类(基类)是type;其二,该元类的类对象仍然是由type进行创建的。
总结本文仍然以“一切皆对象”的概念为出发点及主轴,进行了相关面向对象的知识的补充说明。首先简单介绍了“元”的概念,并通过类比的方式引出来“元类”的概念。其次,回顾了type的定义,并通过代码实例展示了type的两种用法。最后,通过代码的演示,说明了type是一切元类的类,是定义类的默认元类的概念。
需要说明的是,今天的内容更偏向于底层原理的介绍,感兴趣的可以自行进行进一步的深入研究。关于元类的具体使用场景,将在下一篇文章中进行介绍。
感谢您的拨冗阅读,如果对您学习Python有所帮助,欢迎点赞、关注。