这篇文章主要记录自己阅读python源码的一些心得,参考文章如下:
https://realpython.com/cpython-source-code-guide/#why-is-cpython-written-in-c-and-not-python
https://fasionchan.com/python-source/preface/intra/
Doc
Doc文件夹里面包含了所有关于python的内容,比如faq文件夹里面的一些关于python的问题包含了为什么要使用缩进而不是花括号等问题,这个文件夹里面很多文件都是rst格式,可以看作一种跟markdown一样的标记语言,一方面可以直接给人阅读一方面用于计算机渲染文章组织结构,这些文件的实际效果可以在dorcs.python.org看到
里面重要的一些文件夹有:
- faq 一些常见问题以及回答,比如design.rst里回答了为什么要将空格对齐作为语言本身的一部分,为什么python退出时不会释放所有资源,为什么要有list和tuple两种类型…另一值得一看的是programming.rst,里面记录了一些关于python编程的常见问题,比如局部变量的Unbounded Error和lambda表达式总是返回同一个值等,里面一些相关内容可以在我的另一篇博客python拾遗
- reference 有关语言的参考手册,比如模块系统,语法规则,数据模型等
- c-api 有关python的c扩展api
Grammar
这个文件夹主要描述了python语言的文法定义
Grammar文件用一种叫BNF的上下文无关文法描述了整个python的语法细节,是旧版的解析器生成文件,现在使用了一种叫PEG的方法来描述,放在python.gram文件里,Tokens存储了python所有的词法单元,比如关键字,操作符等。是编译python需要用到的文件而不是python执行时会用到的文件,准确的来说该文件执行之后会为Python生成一个语法解析表,python用到的是这个表而不是Grammar文件来判断语法。生成这个表的工具叫pgen,在python源码的Parser文件夹里面有c和python两个版本。比如更改Grammar文件里面的内容pass_stmt: 'pass'为pass_stmt: 'pass' | 'preceed',执行make regen-grammar会调用pgen生成一个新的语法解析表,并重新编译python解释器,输出如下:
# Regenerate Doc/library/token-list.inc from Grammar/Tokens
# using Tools/scripts/generate_token.py
python3.8 ./Tools/scripts/generate_token.py rst \
./Grammar/Tokens \
./Doc/library/token-list.inc
# Regenerate Include/token.h from Grammar/Tokens
# using Tools/scripts/generate_token.py
python3.8 ./Tools/scripts/generate_token.py h \
./Grammar/Tokens \
./Include/token.h
# Regenerate Parser/token.c from Grammar/Tokens
# using Tools/scripts/generate_token.py
python3.8 ./Tools/scripts/generate_token.py c \
./Grammar/Tokens \
./Parser/token.c
# Regenerate Lib/token.py from Grammar/Tokens
# using Tools/scripts/generate_token.py
python3.8 ./Tools/scripts/generate_token.py py \
./Grammar/Tokens \
./Lib/token.py
# Regenerate Include/graminit.h and Python/graminit.c
# from Grammar/Grammar using pgen
PYTHONPATH=. python3.8 -m Parser.pgen ./Grammar/Grammar \
./Grammar/Tokens \
./Include/graminit.h.new \
./Python/graminit.c.new
python3.8 ./Tools/scripts/update_file.py ./Include/graminit.h ./Include/graminit.h.new
python3.8 ./Tools/scripts/update_file.py ./Python/graminit.c ./Python/graminit.c.new
可以很清楚看到哪些文件被更新了,其中用于python解释器进行语法解析的解析表表现为Python/graminit.c,Include/graminit.h,这两个文件会被编译到python解释器中,用于解析python代码
pgen的工作就是根据Grammar文件内容生成一个DFA,这个Python/graminit.c文件就是这个DFA的实现。
另外可以用python的tokenize模块来查看python代码的词法单元,比如下面的代码:
# tokenize_example.py
def add(a, b):
return a + b
执行python -m tokenize tokenize_example.py会输出:
0,0-0,0: ENCODING 'utf-8'
1,0-1,3: NAME 'def'
1,4-1,7: NAME 'add'
1,7-1,8: LPAR '('
1,8-1,9: NAME 'a'
1,9-1,10: COMMA ','
1,11-1,12: NAME 'b'
1,12-1,13: RPAR ')'
1,13-1,14: COLON ':'
1,14-1,15: NEWLINE '\n'
2,0-2,4: INDENT ' '
2,4-2,10: NAME 'return'
2,11-2,12: NAME 'a'
2,13-2,14: PLUS '+'
2,15-2,16: NAME 'b'
2,16-2,17: NEWLINE '\n'
3,0-3,0: DEDENT ''
3,0-3,0: ENDMARKER ''
可以看到有些源文件不可见的符号,比如开头的utf-8以及函数的DEDENT来表示函数结束,ENDMARKER表示文件结束,这些符号都是tokenize模块添加的,用于帮助解析器识别文件的开始和结束,tokenize在Lib/tokenize.py中实现。不过这个模块只是用于分析python代码词法单元,编译器使用的词法解析器是由C实现的,在Parser/tokenizer.c中实现。另外可以使用python -d test.py来查看python解释器的解析过程,具体输出DFA的状态转移过程。
Include
有关python C API的头文件,比如Python.h,这些头文件包含了python解释器的一些基本数据结构,函数声明等,这些头文件会被编译到python解释器中,用于python解释器的编译和链接
Lib
python的标准库实现,全部使用python实现,可以直接导入使用,可以在这里看到很多标准库的python代码实现,是一个学习python代码的好地方
Misc
杂项文件,如配置文件,一些测试文件等
Modules
对python的C扩展,里面有一些与Lib里面相同名字的文件,它们实现了相同的功能但是速度往往更快,这些模块通常被构建成内置模块,以动态链接库的形式存在
Objects
内建对象的实现,比如int,list,dict等,这些对象的实现都在这个文件夹里面,比如int对象的实现在intobject.c中,list对象的实现在listobject.c中。同时包括了对象的创建销毁以及回收等操作。
Parser
包含了python的语法和词法解析实现,其中pgen是传统的解析器,生成一个LL(1)解析器,通过Grammar里的规则生成解析器代码,pegen则是新的解析器,旨在替代原先的pgen。
Python执行流程
Programs/python.c是最开始的入口,里面的Py_Main函数是入口函数,接着进入到Modules/main.c
转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 2128099421@qq.com