[python源码剖析]python总体架构
Python总体架构
在开始剖析python源码之前,我们来看看python是如何架构起来的。
上图左边,是Python提供的大量模块、库以及用户自定义的模块。
中间部分,是python的核心部分---解释器。其中词法分析器(Scanner),将.py源文件或者从命令行输入的Python代码切分为一个个的token;语法分析器(Parser)则是将词法分析器得到的结果进行语法分析,建立抽象语法树(AST);编译器(Compiler)则根据建立的AST生产指令集合-----python字节码(.pyc文件);最后由求值程序来执行这些字节码。
右边,是python运行时的环境。包括对象/类型系统,内存分配器和运行时的状态信息。运行时状态维护了解释器在执行字节码时不同的状态之间切换的动作。内存分配器则负责python创建对象时,对内存的申请工作。而对象/类型系统则包含了python中存在分各种内建对象,如list,dict,str,int等等,以及用户自定义的类型和对象。
python源码的组织
我们从官网上下载python源码,可以看到如下文件结构:
include:该目录下包含了python提供的所有头文件。如果我们需要扩展或者定义自己的头文件,可以添加到该目录下。
Lib:该目录包含了Python自带的标准库。
Modules:该目录包含了所有用C语言写的模块。Modules中的模块是那些对速度要求非常严格的模块,而一些速度没有那么严格的模块,比如OS,就是用python编写的。
Parser:该目录下包含了Python解释器的Scanner和Parser部分,即对python源码进行词法分析和语法分析的部分。
Objects:该目录下包含了Python的内建对象,包括整数,list,dict等。同时该目录下还包含了python在运行时所需的所有的内部使用对象的实现。
Python:该目录下包含了python解释器中的Compiler和执行部分,是python运行的核心所在 。
PCBuild:包含了VS2003的工程文件。也就是说你可以打开这里的文件,自己编译python
PCBuild8:包含了VS2008的工程文件。
一些约定:
在创建对象时,必先分配内存。因此它必须通过内存接口管理,所以在接下来我们会看到源码中这些使用规则。PyObject_GC_New,PyObject_GC_Malloc等API.只要坚持一个原则,凡是以New结尾的,都是以C++中的new操作符视之;凡是以malloc结尾的,都以C中malloc操作符视之。
如上图中,我们约定指针指向的都是离指针最近的那个内存块。
以下补充解释一下上述所提到几个名词:
词法分析器(Scanner)
从左至右逐个字符地对源程序进行扫描,产生一个个单词符号。单词符号一般分为五种:关键字,运算符,界符,标志符和常数
若while种别为01,(种别为11,标识符种别为20,>=种别为12,)种别为13,--种别为14,;种别为30,则经词法分析器处理后,它将被转换为如下的单词符号序列:
( 01, — )
( 11, — )
( 20 ,指向i的符号表项的指针 )
( 12 , — )
( 20 ,指向j的符号表项的指针 )
( 13 , — )
( 20 ,指向i的符号表项的指针 )
( 14 , — )
( 30 , — )
语法分析器(Parser)
根据某种给定的形式文法对由单词序列(如英语单词序列)构成的输入文本进行分析并确定其语法结构的一种过程。语法分析器通常使用一个独立的 词法分析器从输入字符流中分离出一个个的“单词”,并将单词流作为其输入。从开始状态开始,按照文法展开式,逐级进行状态分析,直到分析完毕,如果在此期间出现状态不匹配,即语法错误,停止分析。也就是我们在编译python文件错误信息,就是语法分析器所提供的。
抽象语法树语法树(syntax tree)是源代码的抽象语法结构的树状表现形式。比如,嵌套括号被隐含在树的结构中,并没有以节点的形式呈现;而类似于if-condition-then这样的条件跳转语句,可以使用带有两个分支的节点来表示。
假设对'a + 3 * b'进行解释,其中a=2,b=5
Num = lambda env, n: n
Var = lambda env, x: env[x]
Add = lambda env, a, b: _eval(env, a) + _eval(env, b)
Mul = lambda env, a, b: _eval(env, a) * _eval(env, b)
_eval = lambda env, expr: expr[0](env, *expr[1:])
env = {'a': 2, 'b': 5}
tree = (Add, (Var, 'a'),
(Mul, (Num, 3),
(Var, 'b')))
print _eval(env,tree)
python字节码
'd\x01\x00}\x01\x00|\x01\x00|\x00\x00\x17S'
如上所示,这些就是组成Python字节码的字节。解释器会循环接收各个字节,查找每个字节的指令然后执行这个指令。