前言,这本书比较早,源码剖析是针对python2.5python
python架构主要分为三部分c++
- 从左到右分别为,python文件,python解释器,运行时环境
- python解释器:
- scanner 词法分析,将代码且分为一个个token
- parser语法分析创建AST
- compiler根据AST生成python 字节码
- code Evaluator(虚拟机)执行这些字节码
- 运行时环境:
- 内建对象:list,dict等
- 内存分配器:和malloc的一层接口
- 运行状态信息: 维护解释器在执行字节码时的不一样状态(还不知道具体是怎样的)
- include: python 提供的全部头文件,用于用户经过c或c++来编写自定义模块
- Lib: 包含全部python自带标准库,用python编写的
- Modules: c编写的模块
- parser: 解释器Scanner和Parser部分
- Objects: 内建对象,list, dict,整数等等
- Python: Compiler和执行引擎部分
一些tips: api中以NEW结尾当作c++ new, Malloc结尾的当作c mallorapi
python中类,对象,都是经过对象来实现的数组
- 人的视角: 对象是数据和基于这些数据操做的集合
- 计算机视角: 对象是一片被分配的内存空间(连续或离散),能够在更高层次上做为一个总体来考虑,这个总体就是对象
- python 全部内建类型对象是静态初始化的
- python 一个对象一旦被建立,它内存中的大小就是不可变的(经过指针来定向变长数据)
// object.h
#define PyObject_HEAD \ _PyObject_HEAD_EXTRA \ // 通常状况下为空
int ob_refcnt; // 引用计数
struct _typeobject *ob_type; //指定一个对象类型的类型对象
typedef struct _object{
PyObject_HEAD
} PyObject;
复制代码
- 每个python对象除了PyObject以外还须要额外的内存,PyObject定义了必需要的数据好比PyIntObject
// intobject.h
typedef struct _object{
PyObject_HEAD
long ob_ival;
} PyIntObject;
复制代码
- 对于变长对象python有新的抽象
// object.h
#define PyObject_VAR_HEAD \ PyObject_HEAD \ long ob_size; /通常指容器内元素数量
typedef struct _object{
PyObject_VAR_HEAD
} PyVarObject;
复制代码
PyObject占用内存大小是对象的元信息,元信息和对象所属类型密切相关(下一节介绍)架构
// object.h
typedef struct _typeobject{
PyObject_VAR_HEAD
char *tp_name; //print 信息"<module>.<name>"
int tp_basicsize, item_size; //为了分配内存大小
destructor tp_dealloc;
printfunc tp_print;
hashfunc tp_hash;
ternaryfunc tp_call;
...
} PyTypeObject;
复制代码
python对象的建立主要有两种方式less
- C API(对于python的内建对象, 直接分配内存)
- 泛型API AOL
- 类型相关API COL
- 类型对象建立(用户自定义对象,由于不可能事先提供这类C方法),大体流程以下
- 会调用ob_type 所指定的PyTyoeObject类的 tp_new方法,若是tp_new是NULL会追溯tp_base所指向的ob_type的tp_new方法,最终定位的tp_new(由于全部类继承object会有保底的tp_new方法)负责内存的申请(相似c++的new)
- 以后经过tp_init初始化(相似c++的构造函数)
PyTypeObect 实际也是有ob_type属性的,其为PyType_Type(即type, 负责PyTypeObect的建立,即metaclass)函数
下图以int 为例说明了这些关系,也就是所知的python里面class,object,type的关系 this
其余略lua
主要是PyIntObject和PyInt_Type,其余和广泛的PyObject, PyTyoeObject没什么,值得关注的有spa
- python2中稍小一点的数直接用C语言中的long去存储,稍大一点的数(超过long的承受范围)会使用python的long对象去存储,而python3不会做区分,统一用longObect去存储,实现用到了柔性数组,感兴趣能够查一下
- 小整形数组的内存池和大整形对象的内存链的维护,避免频繁malloc,后面主要介绍下
使用了之前分配好的对象池
// [intobject.c]
#ifndef NSMALLPOSINTS
#define NSMALLPOSINTS 257
#endif
#ifndef NSMALLNEGINTS
#define NSMALLNEGINTS 5
#endif
#if NSMALLNEGINTS + NSMALLPOSINTS > 0
/* References to small integers are saved in this array so that they can be shared. The integers that are saved are those in the range -NSMALLNEGINTS (inclusive) to NSMALLPOSINTS (not inclusive). */
static PyIntObject *small_ints[NSMALLNEGINTS + NSMALLPOSINTS];
#endif
复制代码
对于其余整数,Python运行环境将会提供一块内存空间,这块内存空间由这些对象轮流使用,避免了频繁malloc。 在Python经过PyIntBlock结构,实现这个机制。
// [intobject.c]
#define BLOCK_SIZE 1000 /* 1K less typical malloc overhead */
#define BHEAD_SIZE 8 /* Enough for a 64-bit pointer */
#define N_INTOBJECTS ((BLOCK_SIZE - BHEAD_SIZE) / sizeof(PyIntObject))
struct _intblock {
struct _intblock *next;
PyIntObject objects[N_INTOBJECTS];
};
typedef struct _intblock PyIntBlock;
static PyIntBlock *block_list = NULL;
static PyIntObject *free_list = NULL;
复制代码
能够建立int的代码理解这两块的使用
// [intobject.c]
PyObject * PyInt_FromLong(long ival) {
register PyIntObject *v;
#if NSMALLNEGINTS + NSMALLPOSINTS > 0 /* 尝试使用小整数对象池 */
if (-NSMALLNEGINTS <= ival && ival < NSMALLPOSINTS) {
v = small_ints[ival + NSMALLNEGINTS];
Py_INCREF(v);
return (PyObject *) v;
}
#endif
if (free_list == NULL) {
if ((free_list = fill_free_list()) == NULL)
return NULL;
}
/* Inline PyObject_New */
v = free_list;
free_list = (PyIntObject *)v->ob_type; /* 有一个看似不合适可是比较方便的地方,freelist会经过 ob_type存放可用空间的pyObject的地址(相似链表的next),而不是 PyTyoeObject */
(void)PyObject_INIT(v, &PyInt_Type);
v->ob_ival = ival;
return (PyObject *) v;
}
复制代码
下面是关于freelist的申请,和freelist和block_list的维护有关的代码
// [intobject.c]
static PyIntObject * fill_free_list(void) {
PyIntObject *p, *q;
/* 申请大小为sizeof(PyIntBlock)的内存空间,并连接到已有的block list中 */
p = (PyIntObject *) PyMem_MALLOC(sizeof(PyIntBlock));
((PyIntBlock *)p)->next = block_list;
block_list = (PyIntBlock *)p;
/* 将PyIntBlock中的PyIntObject数组——objects转变成单向链表*/
p = &((PyIntBlock *)p)->objects[0];
q = p + N_INTOBJECTS;
while (--q > p)
q->ob_type = (struct _typeobject *)(q-1); /* 上一段代码中所提到的不合适的地方 Py_TYPE(q) = NULL; return p + N_INTOBJECTS - 1; } 复制代码
这样,freelist会指向能够分配内存的地址,可是若是由以前分配的PyIntObject被释放了,freelist须要将被释放的地址从新使用才能够,这个是经过PyIntObect的析构函数来实现的
// [intobject.c]
static void int_dealloc(PyIntObject *v) {
if (PyInt_CheckExact(v)) { // 若是不是派生类这么执行,保证freelist的完整性
v->ob_type = (struct _typeobject *)free_list;
free_list = v;
}
else // 若是是派生类,则执行正常的析构流程
v->ob_type->tp_free((PyObject *)v);
}
复制代码