本系列是以陈儒先生的《python源码剖析》为学习素材,所总结的笔记。不同的是陈儒先生的《python源码剖析》所剖析的是python2.5,本系列对应的是python3.7。在开始分析python的实现之前,我们有很多的准备工作要做。比如,首先应该了解一下python的整体架构,来对python的实现有一个宏观的认识

0.1 python的总体架构

废话不多说,先来看一张python的总体架构图。

如图所示。在最高的层次上,python的总体架构可以分为三个主要的部分。图的左边,是python所提供的的大量的模块、库、以及用户自定义的模块。比如在执行import os的时候,这个os就是python内建的模块,当然用户还可以通过自定义模块来扩展python系统。

在图的的右边,是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的分析结果上进行语法分析,建立抽象语法树(Abstract Syntax Tree,简称AST );Compiler则是对AST进行编译,生成指令集合--也就是python字节码(byte code);最后由Code Evaluator来执行这些字节码。因此Code Evaluator又可以被称为虚拟机。

Code Evaluator和Object/Type Structure之间的箭头表示使用关系;而与Current State of Python之间的箭头表示修改关系,即python在执行的过程中会不断地修改当前解释器所处的状态,在不同的状态之间切换。

0.2 python源代码的组织

python的源代码可以在python的官网www.python.org中下载,下载完源码的压缩包并解压之后,可以看到如下的目录结构。

Include:该目录包含了python所提供的的所有头文件,如果用户需要自己使用C或者C++来编写自定义模块扩展python,那么就需要用到这里的头文件

Lib:该目录包含了python自带的所有标准库,Lib中的库基本上都是使用python编写的

Modules:该目录中包含了所有用C语言编写的模块,比如random、io等。Modules中的模块是那些对速度要求非常严格的模块,而有一些对速度没有太严格要求的模块,比如os,就是用python编写,并且是放在Lib目录下的。

Parser:该目录中包含了python解释器中的Scanner和Parser部分,即对python源代码进行词法分析和语法分析的部分。除了这些,Parser还包含了一些有用的工具,这些工具能够根据python语言的语法自动生成python语言的词法和语法分析器,与YACC非常类似

Objects:该目录包含了所有python的内建对象,包括整数、list、tuple、dict、set等等。同时,该目录还包含了python在运行时需要的所有内部使用对象的实现。

Python:该目录包含了python解释器中Compiler(编译)和Code Evaluator(执行)两部分,是python运行的核心所在

0.3 修改python源代码

怎么上来就修改python源代码,别慌,只是简单的做一个小小的trick。

//文件:Objects/listobject.c。这是python中与list实现有关的源文件。

//可以看到这个函数主要用来改变列表的容量的。

static int

//关于这里的Py_ssize_t,这是python自定义的类型,就把它当成int即可

list_resize(PyListObject *self, Py_ssize_t newsize)

{

PyObject **items;

size_t new_allocated, num_allocated_bytes;

Py_ssize_t allocated = self->allocated;

//这里的allocated是当前list对象所拥有的容量,注意不是list对象的长度(或者说元素个数),而是容量

//比如 l = [1, 2, 3, 4, 5, 6, 7, 8],如果是使用[]这种中括号的方式创建列表的话。那么显然这里的l的长度和容量都为8。

//而这里的newsize是,当我们append或者extend新元素之后,对应的list对象的长度

//当使用l.append(9)的时候,说明了什么,说明了newsize变成了9

//可是原来的allocated(容量)是8啊,所以要进行扩容了。

//看这里的if条件,显然allocated >= newsize已经不满足了,因为要存储的元素的个数超过了容量,要扩容了

if (allocated >= newsize && newsize >= (allocated >> 1)) {

assert(self->ob_item != NULL || newsize == 0);

Py_SIZE(self) = newsize;

return 0;

}

//扩容之后的容量为new_allocated = newsize + newsize >> 3 + newsize < 9 ? 3 : 6

//所以将9带入进入,得到 9 + 1 + 6 = 16,这便是新分配的容量

/*

l = [1, 2, 3, 4, 5, 6, 7, 8]

print((l.__sizeof__() - 40) // 8) # 8

l.append(9)

print((l.__sizeof__() - 40) // 8) # 16

*/

//使用python进行测试也验证了这一点

new_allocated = (size_t)newsize + (newsize >> 3) + (newsize < 9 ? 3 : 6);

if (new_allocated > (size_t)PY_SSIZE_T_MAX / sizeof(PyObject *)) {

PyErr_NoMemory();

return -1;

}

if (newsize == 0)

new_allocated = 0;

num_allocated_bytes = new_allocated * sizeof(PyObject *);

//将新添加的元素的指针加进列表里面

items = (PyObject **)PyMem_Realloc(self->ob_item, num_allocated_bytes);

if (items == NULL) {

PyErr_NoMemory();

return -1;

}

//让ob_item指向为新的items

self->ob_item = items;

Py_SIZE(self) = newsize;

//将这里的allocated变为新分配的容量

self->allocated = new_allocated;

return 0;

}

// 中间省略了一部分

//可以看到这是通过索引获取list对象内部元素的实现

static PyObject *

list_item(PyListObject *a, Py_ssize_t i)

{

//这里的i就是我们传入的索引。

//如果i小于0或者i大于op(对应的list对象)的最大索引

if (i < 0 || i >= Py_SIZE(a)) {

//当然python中list对象也支持负数索引,因此还会有其他的检测

if (indexerr == NULL) {

//PyUnicode_FromString可以理解为在python代码报错的时候,存储打印信息的函数

indexerr = PyUnicode_FromString(

//看这行代码熟悉不,小伙伴们,索引越界是不是报这个错误啊

"list index out of range");//我们将原本的信息改一下,改成"兄嘚,您列表索引越界了"

if (indexerr == NULL)

return NULL;

}

//设置异常,这是一个IndexError,indexerr则是异常信息

PyErr_SetObject(PyExc_IndexError, indexerr);

return NULL;

}

Py_INCREF(a->ob_item[i]);

//如果i为是正常索引,直接返回。

return a->ob_item[i];

}

0.4 编译python

编译python:只介绍如何在linux下编译python。编译的过程分为三步

进入python的主目录下,执行:./configure -prefix=你期望python安装到的路径

make

make install

其中2、3两步可以合为一步,即make && make install,下面就用我们刚才修改完的python源码进行编译

可以看到异常,已经被我们从底层重新定义了。

0.5 注意事项

在早期版本,python的许多数值的类型都是int或者long,现在Python自己定义了一个新类型,Py_ssize_t,凡出现这个类型的地方,就把它当成int看待即可

python的内存管理机制是比较复杂的,我们会在后面剖析。但是很快你就会看到对内存管理接口的使用。这是因为创建对象必先分配内存,而分配内存必须通过内存管理接口,所以我们在这里提一下。通常Python的源码中会使用PyObject_GC_NEW、PyObject_GC_Malloc、PyMem_MALLOC,PyObject_MALLOC等API。只要坚持一个原则即可,凡是以New结尾的,都当成是C++中new操作符即可,凡是以Malloc结尾的,都当成是C中的malloc操作符即可。

python解释器源码 pdf_《python解释器源码剖析》第0章--python的架构与编译python相关推荐

  1. Ubuntu16.04编译python源码

    软件环境: Ubuntu:版本16.04 Python:版本3.10.2,下载地址https://www.python.org/ftp/python/3.10.2/Python-3.10.2.tgz ...

  2. VS2019编译python解释器源码及学习方法

    Python源码编译   Python是当下很火的一门编程语言,在人工智能.数据分析.后端开发等领域可谓是人人都会的语言,在用python实现各种应用服务的同时,估计很少有人去关注python的实现, ...

  3. 疯狂python讲义视频 百度云-疯狂Python讲义 PDF高清版附源码

    内容简介 本书全面,深入地介绍了Python编程的相关内容,大致可分为四个部分.*系统部分介绍了Python的基本语法结构,函数编程,类和对象,模块和包,异常处理等: 第二部分主要介绍Python常用 ...

  4. 100个Python实战练手项目(附源码+素材),学习必备

    前言: 不管学习哪门语言都希望能做出实际的东西来,这个实际的东西当然就是项目啦,不用多说大家都知道学编程语言一定要做项目才行. 这里整理了最新32个Python实战项目列表,都有完整且详细的视频教程和 ...

  5. 70个Python实用练手项目(附源码)

    不管学习哪门语言都要做出实际的东西来,这个实际的东西就是项目. 恶霸整理了 70 个 Python 实战项目,都有完整且详细的教程,你可以从中选择自己想做的项目进行参考学习练手,你也可以从中寻找灵感去 ...

  6. Python源码剖析[1] —— 编译Python

    [ 绝对原创,转载请注明出处] 注意 :第一部分Python总体架构采用了网络文档<The Architecture of Python>,这是网络上唯一可见的以剖析Python实现为己任 ...

  7. 熬夜整理出了70个清华大佬都在用的Python经典练手项目【附源码】

    我们都知道,不管学习那门语言最终都要做出实际的东西来,而对于编程而言,这个实际的东西当然就是项目啦,不用我多说大家都知道学编程语言做项目的重要性. 于是,小编熬了几个通宵,终于整理出了70个清华大佬都 ...

  8. python编译器源码_编译python源码

    广告关闭 回望2020,你在技术之路上,有什么收获和成长么?对于未来,你有什么期待么?云+社区年度征文,各种定制好礼等你! 尝试通过源码自己编译 python,使用的系统是 ubuntu14.04 l ...

  9. python生日源代码_生日小助手源码运行的步骤

    1.如果您是Ubuntu或者基于Ubuntu的发行版的用户,请通过: sudo apt-get install tcl8.5 sudo apt-get install python-tk 命令,安装环 ...

最新文章

  1. 数据科学竞赛-人脸表情识别
  2. JAVA中几个常用的方法
  3. Apache下PHP Loaded Configuration File None 解决方法
  4. [Github项目]基于PyTorch的深度学习网络模型实现
  5. python 用selenium自动启动百度并搜索关键词
  6. bread是可数还是不可数_为什么英语里的面包bread是不可数名词?听老师给你讲语法,一听就明白了...
  7. linux nmap
  8. Jenkins 学习总结(9)—— Jenkins 有哪些替代方案?
  9. 两道考研算法设计题- 2010 2013
  10. 书生浏览器不能打开这个文件或者url_这些浏览器工作原理你都吃透了吗?
  11. Asp.net MVC的ViewData与ViewBag以及TemplateData的使用与区别
  12. JS Navigator onLine 获取系统是否处于脱机模式
  13. 10首现代诗歌欣赏:什么是孤独
  14. 看这里→大数据工程技术人员系列课程—《大数据工程技术人员-大数据基础技术》正式上线!...
  15. Java经典三角形:杨辉三角
  16. WebService 深入详解
  17. Django中的DateTimeField和DateField
  18. 若不用计算机tan35 怎么算,三角函数计算器-三角函数计算器
  19. svn提交报错,提示“文件或目录损坏且无法读取”,处理方法
  20. vincent歌曲翻译 很美很美很美

热门文章

  1. 冠珠瓷砖打造民族文化品牌,让中国陶成为中国潮
  2. “做教练”之好声音训练
  3. 万字多图 | UML 入门指南
  4. #MEM课程#《公司治理》课后总结
  5. Elsevier期刊中,撰写Author Statement
  6. shiro反序列化漏洞修复
  7. vue:toast组件的设计与优化
  8. CANOpen,关于 DS402 电机驱动器的状态切换
  9. 云计算和大数据的区别与关系
  10. 开源的OA平台、电子政务平台、工作流引擎。