Python源码剖析(0 编译Python)

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

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

0.1 Python总体架构

Python源码剖析(0 编译Python)_第1张图片
在图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)。
Python源码剖析(0 编译Python)_第2张图片

目录 说明
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的工程文件。
Python源码剖析(0 编译Python)_第3张图片
Python源码剖析(0 编译Python)_第4张图片
由于我们剖析的只是Python的核心部分,不会涉及工程中的一些标准库和其他的模块,所以可以将它们从编译的列表中删除。点击配置对话框左边列表框中的“Configuration Properties”后,会出现当前配置为需要编译的子工程,取消多余的子工程的选中状态,只保留pythoncore和python的选中状态。
Python源码剖析(0 编译Python)_第5张图片
编译还是会失败,原因是我们还需要一个必要的文件,这个文件在Python2.5的源码包中没有提供,必须要通过编译make_buildinfo和make_versioninfo子工程(如图0-8所示)才能生成:
Python源码剖析(0 编译Python)_第6张图片
编译的结果都放在build(实际是WIN32DUB文件夹)文件夹下,主要有两个:python25.dll和python.exe。安装还没有解决,也有可能是版本的原因。

0.4 Unix/Linux环境下编译Python

./configure –prefix=<你期望Python安装到的目录的路径> 
make
make 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中给出了一个例子:
Python源码剖析(0 编译Python)_第7张图片
在图0-13中,深色的方块就是指针所指向的内存块


你可能感兴趣的:(python,python)