python源代码学习笔记(一)
这段时间又懒下来了,加上事情也比较忙,完全荒废了这里啊,罪过罪过。
这一系列文章是我根据《Python源码剖析——深度探索动态语言核心技术》学习python源代码的笔记,书中分析的是python2.5的代码,我下载的是python2.6的,有些许不同,不过大致一样。
先来看看python总体的架构吧。如下图(从源码剖析中截图,以下如果未有说明的话都是从书中截的)。

在左边是python和各种模块、库及第三方模块。模块一般是由python编写而成,不过在一些对速度要求比较严格的地方,也可以用C写,python和C的兼容性是相当好的恩。不过这一块并不是学习的主要部分,它对应着python docs里的Library Reference,而书中主要讲解的是Language Reference的部分。图的右边是python的运行环境,可以理解为系统运行一个进程时堆栈,寄存器等所保存的上下文环境。中间是python虚拟机,所有的代码都是在它上面运行的。在虚拟机的上层,scanner, parser, compiler其实和一般的编译过程没有太大区别,只不是一般的C编译器最终是将代码翻译成机器码,再由操作系统作为运行环境来执行;而python则是把代码翻译成字节码,以python虚拟机来作为运行环境而执行。python解释器和python运行环境是我们学习的重点。
让我们从python中的对象开始吧。python中一切都是对象。从内建的int、string到用户自定义的class,这些都是对象。进一步的,它们的类型也是对象。对于一般的对象来说,它们是不能被静态初始化的,并且都是在堆上,但有一些例外,就是内建的类型对象,比如int类型这个对象,它们是被静态初始化并在栈上分配内存的。python中的对象一旦被创建,在内存中的大小就再也不变化了,这样是为了保证设计的简单性,其实这种思想在各种地方都是有体现,比如hbase对外提供的接口,也就是get, put, scan不多几个,如果在保证可扩展性和可靠性的前提下还要设计诸如关系型数据库中的那些比如事务性,原子性等,那将会相当复杂。
对象所对应的类型定义在object.h中。代码如下
typedef struct _object
{
PyObject_HEAD
} PyObject;
#ifdef Py_TRACE_REFS
/* Define pointers to support a double-linked list of all live heap objects. */
#define _PyObject_HEAD_EXTRA \
struct _object *_ob_next;
struct _object *ob_prev;
#define _PyObject_EXTRA_INIT 0, 0,
#else
#define _PyObject_HEAD_EXTRA
#define _PyObject_EXTRA_INIT
#endif
/* PyObject_HEAD defines the initial segment of every PyObject. */
#define PyObject_HEAD \
_PyObject_HEAD_EXTRA \
Py_ssize_t ob_refcnt; \
struct _typeobject *ob_type;
上面的代码可以简化为
typedef struct _object
{
int ob_refcnt;
struct _typeobject *ob_type;
} PyObject;
ob_refcnt是用来实现引用户数的gc。ob_type指向了这个对象的类型对象,换句话说,它存储了这个对象的类型。对象可以分为两种,一种是定长对象,比如int,另一种是变长的对象,比如list。对于前一种,只用在实际的对象中再分配一个固定的区域用来存储值就可以了,但这种方法对后一种对象就失效了,因此python中定义了表示变长对象的结构体。
#define PyObject_VAR_HEAD \
PyObject_HEAD \
Py_ssize_t ob_size;
typedef struct
{
PyObject_VAR_HEAD
} PyVarObject;
可以看出来,其实就是将一般的对象包装了一下,加了一个ob_size。表示所容纳的元素个数,比如一个list里面有5个string的话,那这个size就为5。从这里我们可以看出,只需要一个PyObject *指针就可以引用任何一个对象。这对于python实现各种语言特性很关键。接下来我们来看看_typeobject中到底有什么。
typedef struct _typeobject
{
PyObject_VAR_HEAD
const char * tp_name; /* For printing, in format "<module>.<name>" */
Py_ssize_t tp_basicssize, tpitemsize; /* For allocation */
/* Methods to implement standard operations */
destructor tp_dealloc;
printfunc tp_print;
....
/* Method suites for stadard classes */
PyNumberMethods *tp_as_number;
PYSequenceMethods *tp_as_sequence;
PyMappingMethods *tp_as_mapping;
/* More stadard operations (here for binary compatibility) */
hashfunc tp_hash;
....
} PyTypeObject;
首先可以看到,这个类型变量也是一个对象,因为它有PyObject_VAR_HEAD,接下来的tp_name是它的类型名。tp_basicsize和tb_itemsize给定了分配内存时的相关信息。在这个结构体中还主要定义了三类方法,一是基本操作,比如打印对象,析构对象等。一类是对于在用运算符做运算时所应该调用的函数,比如加减、取键值等等。还有一类比如使用hash(oneobject)时调用的函数。其实这个类中还有很多信息,不过为了简明起见,先列出这些。
我们已经知道,一切都是对象,每个对象都有类型。那么对一个int来说,它的类型是
PyTypeObject PyType_Type =
{
PyVarObject_HEAD_INIT(&PyType_Type, 0)
"type", /* tp_name */
sizeof(PyHeapTypeObject), /* tp_basicsize */
sizeof(PyMemberDef), /*tp_itemsize*/
...
}
看看整数对象是怎么和它联系起来的。
PyTypeObject PyInt_Type =
{
PyVarObject_HEAD_INIT(&PyType_Type, 0)
"int",
...
}
下图可以更好地说明这个问题
