阅读条件:

C语言基础,Python对象模型基本了解


前言

在深入学习Python的过程中,不可避免地会接触到这样的概念:

在Python中一切都是对象

这个一切甚至比我想象的还要深入,因为在Python的实现层面也都是用面向对象实现的.那么与对象模型相关的,实例的概念是不是也是用面向对象实现的呢?是的,在Python中,类的定义本身也是一个对象.

从实现层面来看,我认为只要能用一个PyObject*来指向的结构体都是对象.我认为Python中的对象可以分为以下几类:

  1. 普通内建类对象,不需要开发者实现,由系统提供的类对象,比如int, list等.
  2. 特殊内建类对象,不要开发者实现,但是在对象模型中具有特殊功能,其实也就是两个特殊对象,type和object.
  3. 自定义类对象,开发者实现的类所生成的对象.
  4. 实例对象,由类对象实例化所生成的对象.

下面我们来分别讲讲这些过程.

示意图

下面这幅是从网上摘抄过来的.也是经典Python对象模型示意图.

Python内存对象间的关系

三个框分别表示元类,类型,一般对象.其中虚线表示类与实例的关系,实线表示父类与子类的继承关系.

创世纪的三个过程

在Python虚拟机中叱咤风云的往往是一个个实例对象.但是在创造他们之前,我们需要经过漫长的过程.了解这个过程,将极大地帮助我们了解元类.

内建类对象的初始化

在CPython中,所有的内建类对象表现为一个个C语言结构体,而且都是静态初始化的全局变量.所以他们几乎天然就是一个PyObject对象了.为什么说几乎呢?因为还需要对他们进行一点点改造.

这个过程的实现在Objects/typeobject.cPyType_Ready中.这个函数主要完成如下工作:

  1. 确定本类的基类
  2. 递归地初始化基类
  3. 确定本类的类型
  4. 填充本类的__dict__
  5. 从基类中的继承
  6. 将本类加入基类的子类列表

这里面值得关注的是第4步和第5步.在完成了这些工作之后,内建对象的构建就完成了.

我们在多说两句,关于特殊内建对象.object对象是所有类对象的基类,也是第一个被初始化的.而type对象是绝大多数对象的类型对象.

自定义类对象的生成

一个自定义的类对象的生成,要经过几个步骤呢?首先,要准备材料.主要是自定义类的名字,自定义类的基类列表以及自定义类的属性列表.这里变量和方法都可以归为属性.然后,就是调用元类了.主要代码Objects/typeobject.c/type_call一路追下去.

步骤如下:

  1. 调用metaclass->ob_type->tp_call,也就是type_call.
  2. 调用metaclass->tp_new,也就是type_new.
  3. 调用metaclass->tp_alloc,这个方法type本身没有定义,是从object继承而来,所以是object->tp_alloc.也就是PyType_GenericAlloc.
  4. 初始化新建的type.为其中的一些域赋值.
  5. 调用PyType_Ready.
  6. 调用fixup_slot_dispatchers,这个很有意思.是处理重载的关键.如果你覆盖了基类的方法,会在这里实现.
  7. 调用metaclass->tp_init,这个方法同样从object继承,也就是object_init.

实例对象的生成

主要步骤如下:

  1. 调用class->ob_type->tp_call,一般来讲就是type_call.
  2. 调用class->tp_new,这个方法是从object继承而来,也就是object_new.我们看到这也是创建类对象和实例对象的主要区别点.
  3. 调用class->tp_alloc,同样是PyType_GenericAlloc.
  4. 调用class->tp_init.我们经常定义的def __init__就是在这里调用的.

基类和类型在Python中的作用

在弄清楚一系列对象的创建过程后,在更高的维度我是怎么看的呢? object是一切类对象的基类,是众基之基.所有对象如果没有重写的话,那么所有对象都会从object中继承方法.所以object最重要的作用是什么呢?就是把所有类都要具有的方法,抽出来,聚集到一起.这就是object.

实例是类对象实例化的结果.同一类对象的各个实例之间其实大体相似,区别只是个别属性.而类对象之间的结构也是大体相似的,而元类就是各个类对象的类.

通过重载元类的__new__,__init__,我们能够控制类对象的生成.重载__call__,我们能够控制实例对象的生成.

总结

现在总算基本弄清楚Python的对象模型.这块还是有点复杂的.我这里其实就是自己的小结.更多地内容还是得去看书,看源码.