Python源码剖析(0 编译Python)

来源:互联网 发布:数组词多音字组词 编辑:程序博客网 时间:2024/05/29 10:38

    • 1 Python总体架构
    • 2 Python源代码的组织
    • 3 Windows环境下编译Python
    • 4 UnixLinux环境下编译Python
    • 5 修改Python源代码
    • 6 通往Python之路
    • 7 一些注意事项

这一系列的资料来源:Python源码剖析

0.1 Python总体架构

图0_1 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在执行的过程中会不断地修改当前解释器所处的状态,在不同的状态之间切换。

0.2 Python源代码的组织

剖析的对象是2006年12月19日正式发布的Python 2.5(2.5衍生版都差不多,自己用的好像是2.5.6)。
图0_3 Python目录结构

目录 说明 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使用的工程文件。

0.3 Windows环境下编译Python

在PCBuild目录下可以看到VS2003的工程文件,PCBuild8目录下是VS2005的工程文件。
图0_4 调出设置属性对话框
图0_5 改变startup project
由于我们剖析的只是Python的核心部分,不会涉及工程中的一些标准库和其他的模块,所以可以将它们从编译的列表中删除。点击配置对话框左边列表框中的“Configuration Properties”后,会出现当前配置为需要编译的子工程,取消多余的子工程的选中状态,只保留pythoncore和python的选中状态。
图0_6 取消不相关子工程
编译还是会失败,原因是我们还需要一个必要的文件,这个文件在Python2.5的源码包中没有提供,必须要通过编译make_buildinfo和make_versioninfo子工程(如图0-8所示)才能生成:
图0_8 2个工程
编译的结果都放在build(实际是WIN32DUB文件夹)文件夹下,主要有两个:python25.dll和python.exe。安装还没有解决,也有可能是版本的原因。

0.4 Unix/Linux环境下编译Python

./configure –prefix=<你期望Python安装到的目录的路径> makemake install

2.5安装错误。和2.6及以上的安装路径,库名均会不同。

0.5 修改Python源代码

在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值。

0.6 通往Python之路

将对Python源码的剖析分为下面三个部分:

  • 第1部分:Python内建对象。主要内容是简要介绍Python对象模型,以及剖析主要内建对象,包括整数、字符串、list和dict。在对内建对象的剖析中,我们会深入其实现,细致地分析各种对象在C一级是如何被构建起来的。
  • 第2部分:Python虚拟机。主要内容是分析Python虚拟机执行字节码指令的过程。在这一部分中,我们将看到Python是如何通过虚拟机实现各种表达式、控制流、异常机制、函数机制及类机制.
  • 第3部分:Python高级话题。主要内容是剖析Python的运行环境以及一些高级话题。内容包括:Python运行环境的初始化、动态加载机制、多线程机制和内存管理机制。

0.7 一些注意事项

在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 指针示意图
在图0-13中,深色的方块就是指针所指向的内存块


原创粉丝点击