接触python有段时间了,说实话,当我第一次用的时候就喜欢上了这门语言,那种编程的流畅感真的让人耳目一新。但这将近一年来,我只是用python小打小闹的写个小游戏,分析个数据,还参加了一次数学建模比赛,并没有系统的了解它。最近突然想要深入学习这门语言,所以找了一些资料,意外发现了一本陈儒先生写的《python源码分析》,就干脆跟着这本书从源码开始,认真了解这门语言。同时,通过博客和大家分享一下我的学习经历
一 认识源码
俗话说,“巧妇难为无米之炊”,要分析源码首先要得到源码。当然,python的源码很容易获得,这里给出官网的下载地址:
https://www.python.org/ftp/python/2.7.6/Python-2.7.6.tgz
虽然书中分析的是几年前的2.5版本的python,但是本着与时俱进的原则,我们还是用最新版本的2.7.6的源码。
解压缩后的目录结构如下:
介绍几个主要模块:
Include:python用到的所有的头文件,如果你想过用C/C++模块拓展python,就可以用到这里的头文件
Lib:这里包含了所有用python语言编写的内部标准库
Modules:包含了所有用C语言编写的模块,主要是一些对速度要求比较高的模块,如readline、tkinter等
Parser:包含了python的解释器中scanner和parser等部分,用来对python代码进行句法分析,此外还有一些很给力的小工具,能自动生成python词语法分析器
Objects:包含了所有python的内建对象,包括整数、list、dict等
Python:包含了python解释器的Compiler和执行引擎部分,从名字也看得出,是python的核心所在
Tools:一看就知道,这是各种工具的集合
Pcbuild:Visual Studio的工程文件,分析源码时很有用
二 python内建对象
了解python的人都知道,python中所有的东西都被当作对象处理,变量、函数甚至数据类型都无一例外,python中大量的内建对象也无疑是python核心的重要组成部分,也正是python设计哲学的体现,因此,我们就从内建对象开始,一步步征服python源代码。
首先,我们打开刚才的Include文件夹,找到object.h 头文件,看看python中的对象是如何定义的。
Python中,所有的东西都是对象,而所有的对象都拥有相同的内容,这些都定义在PyObject结构体中,可以说是python对象机制的核心:
[object.h] Typedef struct_object{ PyObject_HEAD }PyObject;
事实上,python对象所有的秘密都藏在这个叫PyObject__HEAD的宏中:
[object.h] #ifdef Py_TRACE_REFS /* Define pointers to support a doubly-linked list of all live heap objects. */ #define _PyObject_HEAD_EXTRA \ struct _object *_ob_next; \ struct _object *_ob_prev; #define _PyObject_EXTRA_INIT 0, 0, #else #define _PyObject_HEAD_EXTRA #define _PyObject_EXTRA_INIT #endif /* PyObject_HEAD defines the initial segment of every PyObject. */ #define PyObject_HEAD \ _PyObject_HEAD_EXTRA \ Py_ssize_t ob_refcnt; \ struct _typeobject *ob_type;
这里涉及到了条件编译的概念,翻译一下:
1 如果定义了 Py_TRACE_REFS:
/* 定义一个指针指向一个保存所有堆中存活的对象的双向链表 */
_PyObject_HEAD_EXTRA 替换为两行代码 (其中 \ 符用来续行):
struct _object *_ob_next; struct _object *_ob_prev;
定义 _PyObject_EXTRA_INIT 为 0, 0,
2 如果没定义Py_TRACE_REFS:
_PyObject_HEAD_EXTRA 与 _PyObject_EXTRA_INIT都定义为空。(防止报错)
/* PyObject_HEAD 定义了每个Pyobject的初始化部分 */
PyObject_HEAD 替换为三行代码:
_PyObject_HEAD_EXTRA; Py_ssize_t ob_refcnt; struct _typeobject *ob_type;
当我们在vs的release模式下编译时,不会定义Py_TRACE_REFS。 因此,最终的PyObject定义很简单:
[object.h] Typedef struct_object{ Py_ssize_t ob_refcnt; struct _typeobject *ob_type; }PyObject;
在这里,我们不妨用vs的追踪一下这里的Py_ssize_t,看看他到底是什么。用vs打开PcBuilt文件夹中的解决方案,打开pythoncore工程/外部依赖项/object.h,找到但蓝色字体标记的Py_ssize_t,按F12转到定义:
[pyport.h] typedef ssize_t Py_ssize_t;
继续追踪:
[pyconfig.h] typedef _W64 int ssize_t;
这里的_W64是为了兼容64位系统,可以忽略,所以Py_ssize_t本质就是int。因此,最终的PyObject的定义为:
[object.h] Typedef struct_object{ int ob_refcnt; struct _typeobject *ob_type; }PyObject;
2 变量的作用
下面,我们分析一下PyObject中的两个参数:
1 ob_refcnt: 实质是int整数,用来实现垃圾管理机制,代表对象的引用次数,当它为零时,说明没有任何 变量名引用这个对象了,因而对象会被处理掉,腾出内存
2 ob_type:类型对象,用来指向表示对象类型。怎么,不懂?别急,留个伏笔,后面我会详细讲。
所以python 中对象机制的核心很简单:一个引用计数,一个类型信息。
3 其他的部分
作为python对象机制的基础,pyobject定义了所有对象都必须有的部分,它占据了每个对象内存空间的最前端。最前端,意思就是除了Pyobject外,每个对象还应该占有更多的内存空间,还应该有自己“其他的部分”了?这是肯定的,因为要是所有的对象都只包含这两个东西,那么岂不是只有一种对象了,这太不现实了。
那么,这“其他的部分”又有什么呢?
[intobject.h] typedef struct{ Pyobject_HEAD long ob_ival; }PyVarObject;
这是python中的整数对象的定义,它里面除了Pyobject_HEAD外,还有一个long型的变量ob_ival,不用说你大概也猜到了,这是整数的数值。同理,字符串对象,list和dict对象,还有其他成千上万的对象,都是这种模式,即Pyobject_HEAD + 自己的“其他的部分”。
4 变长与定长对象
我们知道,int在python只是一个整型变量,他的“其他东西”就是一个int型数值ob_ival,他占的内存大小是固定的,因此被称为“定长对象”。当然,与之对应的,python中同样会有“变长对象”,即他们的内存长度会随着情况不同而改变,举个很简单的例子:字符串。与int类似,python中的字符串的“其他的部分”显然应该是“一个字符串”。当然,我们都知道,C语言中没有“一个字符串”的概念的,只有“N个char型变量的数组”。但是,即使是这样,这N也是个不确定因素,“python”与“java”占内存大小不会是一样的,与之类似的明显还有包含“N个Object对象”的list、“N个Key—Value”的dict,这些,就是“变长对象”。
那这个变长对象python又是怎么描述的呢?接着往下看:
[object.h] #define PyObject_VAR_HEAD \ PyObject_HEAD \ Py_ssize_t ob_size; /* Number of items in variable part */ … typedef struct { PyObject_VAR_HEAD } PyVarObject;
原来,在PyObject对象之外,还有一个表示对象的结构体——PyVarObject。
我们看到,PyVarObject中多了一个ob_size变量,它的作用就是用来表示上面的N,用来改变对象的长度。有一点要注意的,那就是ob_size 代表的是对象中的元素个数,而不是对象占的字节数,比如“abc”的N为4(别忘了结束符\0),[1,2,3,4,5]的N为5。
而从PyVarObject的定义看出,他只是一个PyObject的拓展,其开始部分与PyObject相同,都是Pyobject_HEAD,因此,python中的对象引用非常统一,使用一个PyObject*类型的指针就可以调用任意一个对象。
据此,我们有理由推测,python中变量不用声明类型,拿来即用的强大功能,正是源自于此。
5 类型对象
目前,我们知道,通过PyVarObject的ob_size变量,可以改变变长对象的内存长度。然而,我们还并没有看到python具体是怎么做到这点的。还记不记得,在前面讲PyObject_HEAD的定义时,我埋了一个伏笔,那就是_typeobject *ob_type,那个表示对象类型的指针。
现在,我们转到 _typeobject的定义:
typedef struct _typeobject { PyObject_VAR_HEAD const char *tp_name; /* For printing, in format "<module>.<name>" */ Py_ssize_t tp_basicsize, tp_itemsize; /* For allocation */ /* Methods to implement standard operations */ destructor tp_dealloc; printfunc tp_print; ... /* More standard operations (here for binary compatibility) */ hashfunc tp_hash; ternaryfunc tp_call; ... } PyTypeObject;
承认信息量有点大,简单梳理一下,主要有四条信息:
1 类型名,tp_name,格式为<module>.<name>,用于内部调用和方便调试
2 创建对象时分配内存空间大小的变量, tp_basicsize和tp_itemsize,分别代表基本的大小和里面元素的 大小
3 与对象相关的操作,如tp_print这样的函数
4 要描述的本类型的其他信息
不知道你们有没有发现,这里的PyTypeObject结构体,实际上实现了面向对象编程里“类”的概念,不仅是因为它包含变量属性和方法,背后还有很多学问,再此同样埋个伏笔,后面会结合python的类型与对象体系详细分析。
好了,今天先写这些,下篇继续。
相关推荐
本书以CPython为研究对象,在C代码一级,深入细致地剖析了Python的实现。书中不仅包括了对大量Python内置对象的剖析,更将大量的篇幅用于对Python虚拟机及Python高级特性的剖析。这本书的纸质版早就绝版了,这是我拿...
《Python源码剖析》这本书是Python开发者深入理解Python内核的重要参考资料。虽然描述中提到PDF版本的清晰度不是特别高,但仍然是一个宝贵的资源,尤其是对于那些想了解Python内部运作机制的开发者而言。这本书涵盖...
python源码剖析 pdf
Python源码剖析是深入理解Python这门编程语言的关键途径。Python作为一种广泛应用于科学计算、数据分析、人工智能、网站开发等领域的高级编程语言,其源代码的分析能够帮助开发者理解Python的内部工作原理,提升编程...
以下是对Python源码剖析的一些关键知识点: 1. **解释器**: Python的执行流程始于解释器,如CPython(官方的Python实现)。它将源代码转换为字节码,然后由虚拟机执行。理解解释器的工作原理,包括词法分析、语法...
目前在网最清晰扫描版本,总文件360M,CSDN限制上传文件大小为110M,故原样拆分为6部分,ABCDE部分各含90个PDF页面,F部分含63个PDF页面,本文件对应真实书籍页码封面至74页,积分设置20分,因为这本书值。
Python源码剖析(清晰影印版)
前两天买了《python 源码剖析》发现这么好的书居然没有配套源代码,而现在PYTHON的版本已经到3了,书中用的源代码是2.5.2,这份源代码网上虽然可以找到,但是编译起来根本没有书里面说的轻松,再忙活了将尽一天之后...
### Python源码剖析笔记知识点梳理 #### C语言基础回顾 ##### 关于ELF文件 ELF(Executable and Linkable Format)文件是Linux下一种常见的可执行文件格式。它分为可执行文件和目标文件两种类型,其中可执行文件...
前两天买了《python 源码剖析》发现这么好的书居然没有配套源代码,而现在PYTHON的版本已经到3了,书中用的源代码是2.5.2,这份源代码网上虽然可以找到,但是编译起来根本没有书里面说的轻松,再忙活了将尽一天之后...
python源码剖析,帮助你从底层了解python,了解运行机制,方法的实现
资源名称:Python源码剖析——深度探索动态语言核心技术资源截图: 资源太大,传百度网盘了,链接在附件中,有需要的同学自取。