这一系列的资料来源:Python源码剖析
在图0-1的右边,是Python的运行时环境,包括对象/类型系统(Object/Type structures)、内存分配器(Memory Allocator)和运行时状态信息(Current State of Python)。运行时状态维护了解释器在执行字节码时不同的状态(比如正常状态和异常状态)之间切换的动作,我们可以将它视为一个巨大而复杂的有穷状态机。内存分配器则全权负责Python中创建对象时,对内存的申请工作,实际上它就是Python运行时与C中malloc的一层接口。而对象/类型系统则包含了Python中存在的各种内建对象,比如整数、list和dict,以及各种用户自定义的类型和对象。
在中间的部分,可以看到Python的核心——解释器(interpreter),或者称为虚拟机。在解析器中,箭头的方向指示了Python运行过程中的数据流方向。其中Scanner对应词法分析,将文件输入的Python源代码或从命令行输入的一行行Python代码切分为一个的token;Parser对应语法分析,在Scanner的分析结果上进行语法分析,建立抽象语法树(AST);Compiler是根据建立的AST生成指令集合——Python字节码(byte code),就像Java编译器和C#编译器所做的那样;最后由Code Evaluator来执行这些字节码。。因此,Code Evaluator又可以被称为虚拟机。
图中,在解释器与右边的对象/类型系统、内存分配器之间的箭头表示“使用”关系;而与运行时状态之间的箭头表示“修改”关系,即Python在执行的过程中会不断地修改当前解释器所处的状态,在不同的状态之间切换。
剖析的对象是2006年12月19日正式发布的Python 2.5(2.5衍生版都差不多,自己用的好像是2.5.6)。
目录 | 说明 |
---|---|
Include | 该目录下包含了Python提供的所有头文件,如果用户需要自己用C或C++来编写自定义模块扩展Python,那么就需要用到这里提供的头文件。 |
Lib | 该目录包含了Python自带的所有标准库,Lib中的库都是用Python语言编写的。 |
Modules | 该目录中包含了所有用C语言编写的模块,比如random、cStringIO等。Modules中的模块是那些对速度要求非常严格的模块,而有一些对速度没有太严格要求的模块,比如os,就是用Python编写,并且放在Lib目录下的。 |
Parser | 该目录中包含了Python解释器中的Scanner和Parser部分,即对Python源代码进行词法分析和语法分析的部分。除了这些,Parser目录下还包含了一些有用的工具,这些工具能够根据Python语言的语法自动生成Python语言的词法和语法分析器,与YACC非常类似。 |
Objects | 该目录中包含了所有Python的内建对象,包括整数、list、dict等。同时,该目录还包括了Python在运行时需要的所有的内部使用对象的实现。 |
Python | 该目录下包含了Python解释器中的Compiler和执行引擎部分,是Python运行的核心所在。 |
PCBuild | 包含了Visual Studio 2003的工程文件,研究Python源代码就从这里开始(本书将采用VS2003对Python进行编译)。 |
PCBuild8 | 包含了Visual Stuido 2005使用的工程文件。 |
在PCBuild目录下可以看到VS2003的工程文件,PCBuild8目录下是VS2005的工程文件。
由于我们剖析的只是Python的核心部分,不会涉及工程中的一些标准库和其他的模块,所以可以将它们从编译的列表中删除。点击配置对话框左边列表框中的“Configuration Properties”后,会出现当前配置为需要编译的子工程,取消多余的子工程的选中状态,只保留pythoncore和python的选中状态。
编译还是会失败,原因是我们还需要一个必要的文件,这个文件在Python2.5的源码包中没有提供,必须要通过编译make_buildinfo和make_versioninfo子工程(如图0-8所示)才能生成:
编译的结果都放在build(实际是WIN32DUB文件夹)文件夹下,主要有两个:python25.dll和python.exe。安装还没有解决,也有可能是版本的原因。
./configure –prefix=<你期望Python安装到的目录的路径>
make
make install
2.5安装错误。和2.6及以上的安装路径,库名均会不同。
在int_print中输出一个整数,可以将int_print修改成如下的函数:
[intobject.c] 在objects文件夹下
static int int_print(PyIntObject *v, FILE *fp, int flags)
{
//add by Robert
PyObject* str = PyString_FromString("i am in int_print");
PyObject_Print(str, stdout, 0);
printf(“\n”);
fprintf(fp, "%ld", v->ob_ival);
return 0;
}
输出:
>>>print 100
‘i am in int_print’
100
在PyObject_Print中,第二个参数指明的是输出目标。上面的例子使用了stdout,指定了输出目标为标准输出,当我们从命令行环境中激活Python时,没有问题,但是如果使用IDLE的话,就会发现,输出的信息没有了。原因是IDLE的输出目标已经不是stdout了,说明加入的输出代码失效了.
在Python中,有一个特性——可以自己重定向标准输出。下面的代码显示了如何输出到重定向后的标准输出:
static PyObject* int_repr(PyIntObject *v)
{
char buf[64]
//add by Robert
If(PyInt_AsLong(v) == -999) {
PyObject* str = PyString_FromString("i am in int_repr");
PyObject* out = PySys_GetObject("stdout");
if(out != NULL) {
PyObject_Print(str, stdout, 0);
printf("\n");
}
}
……
}
PyInt_AsLong的功能是将Python的整数对象转换为C中的int值。
将对Python源码的剖析分为下面三个部分:
在Python 2.4的源码中,许多数值的类型都是int或long,而在Python 2.5的源码中,Python自定义了一个新的类型Py_ssize_t。一般的,凡出现这个类型的地方,都可以以int视之。
通常Python的源码中会使用PyObject_GC_New、PyObject_GC_Malloc、PyMem_MALLOC,PyObject_MALLOC等API。只要坚持一个原则,即凡是以New结尾的,都以C++中的new操作符视之;凡是以Malloc结尾的,都以C中的malloc操作符视之。
[PyString_FromString() in stringobject.c]
op = (PyStringObject *)PyObject_MALLOC(sizeof(PyStringObject) + size);
等效于:
PyStringObject* op = (PyStringObject*)malloc(sizeof(PyStringObject)+size);
[PyList_New() in listobject.c]
op = PyObject_GC_New(PyListObject, &PyList_Type);
等效于:
PyListObject* op = new PyList_Type();
op->ob_item = (PyObject **) PyMem_MALLOC(nbytes);
等效于:
op->ob_item = (PyObject **)malloc(nbytes);
在本书中,指针指向的内存块都是距离指针最近的向右或向下的那块内存,图0-13中给出了一个例子:
在图0-13中,深色的方块就是指针所指向的内存块