Python源码分析(一)
最近想学习下Python的源码,希望写个系列博客,记录的同时督促自己学习。
Python源码目录
从Python.org中下载源代码压缩包并解压,我下载的是Python2.7.12,解压后:

对于主要的文件夹做出介绍:
Include:包含Python提供的所有头文件,如果需要自己使用C或者C++编写自定义模块扩展Python,就需要用到这里的头文件;
Lib: 包含Python自带的所有标准库,全部由Python语言编写;
Modules:包含了所有使用C语言编写的模块;
Parser:Python解释器中的Scanner和Parser(对Python代码进行词法分析和语法分析),这里还包含一些能根据Python语言的语法自动生成Python词法和语法功能的工具;
Objects:所有Python的内建对象;
Python:Python解释器中的Compiler和执行引擎部分,是Python运行的核心所在!!!
Python中的对象
对象可以说是Python最核心的一个概念,在Python的世界里,一切都是对象。我们知道Python是由C编写的,C并不是一个面向对象语言,而由C编写的Python确实面向对象的,那么它的对象机制是如何实现的呢?
对于人的思维,对象是可以形象描述的,但是对于计算机而言,对象是一个抽象的概念,计算机所知道的一切都是字节。关于对象,通常的说法是,对象是数据以及基于这些数据的操作的集合,在计算机中,一个对象实际就是把一片被分配的内存空间,且这片内存在更高层次可作为一个整体,这个整体就是一个对象。
在Python中,对象就是C中的结构体在堆上申请的一块内存。
对象机制的基石——Pyobject
在Python中,所有的东西都是对象,而所有的对象都拥有一些相同的内容,Python中的这些内容都是在object.h中的Pyobject中定义的。
typedef struct _object {
PyObject_HEAD
} PyObject;
定长对象和变长对象
Python除了Pyobject对象之外,还有一个表示这类对象的结构体pyVarObject,pyVarobject其实就是对pyobject的一个扩展。
那么站在源码的角度上分析,变长对象是在pyVarobject中添加了可变长度数据的对象,也就是ob_size,定义了所容纳元素的个数。定长对象和变长对象的区别是:定长对象的不同对象占用的内存大小是一样的,变长对象的不同对象占用的内存可能是不一样的。比如整数对象'1'和'100'占用的内存大小都是sizeof(PyIntObject),而字符串对象"me"和"you"占用的内存大小就不一样。
Python对象的多态性
面向对象中一个重要的特性是多态,那么Python是如何实现多态的呢?
在Python创建一个对象时,会分配内存,进行初始化,然后Python内部会使用一个PyObject*变量来保存和维护这个对象,Python中的所有对象均是如此。比如创建一个PyIntObject对象(整数对象),不是通过PyIntObject *变量来保存和维护这个对象,而是通过PyObject *,正因为所有对象均如此,所以Python内部各个函数之间传递的都是一种范型指针(Pyobject*),而这个指针所指的对象究竟是什么类型的,我们是不知道的,只能从指针所指对象的ob_type域动态进行判断,而正是这个域,Python实现了多态。
引用计数
和C或C++不同,Python选择使用语言本身负责内存的管理和维护,也就是垃圾收集机制,代替程序员进行繁重的内存管理工作,而引用计数刚好是Python垃圾收集机制的一部分。
Python通过对一个对象的引用计数来管理和维护对象在内存中的存在与否。Python中的一切皆是对象,在所有的对象中有一个ob_refcent变量,维护这对象的引用计数,从而也决定该对象的创建与消亡。
在Python中,使用Py_INCREF(op)和Py_DECREF(op)两个宏来增加和减少一个对象的引用计数,在每一个对象创建的时候,Python提供了一个Py_NewReference(op)宏来将对象的引用计数初始化为1。
当一个对象的引用计数为0时,与该对象对应的析构函数将被调用, 但是调用析构函数并不一定是调用free释放内存空间,为了避免频繁的申请、释放内存空间,Python中使用的是内存对象池,维护一定大小的内存对象池,调用析构函数时,对象占用的空间将归还到内存池中。
Python中的整数对象
在Python的所有对象中,整数对象最简单且使用最频繁,故我们首先学习整数对象。关于整数对象的源码在Objects.intobjects.c中,整数对象是通过PyIntObject对象来完成的,在创建一个PyIntObject对象之后,就再也不能改变该对象的值了。定义为:
typedef struct {
prObject_HEAD;
long ob_ival;
}PyIntObject;
可以看到,Python中的整数对象其实是对C中long的一个简单封装,也就是整数对象维护的数据的长度在对象定义时就已经确定了,就是C中long的长度。
在Python中,整数的使用是很广泛的,对应的,它的创建和释放也将会很频繁,那么如何设计一个高效的机制,使得整数对象的使用不会成为Python的瓶颈?在Python中是使用整数对象的缓冲池机制来解决此问题。使用缓冲池机制,那意味着运行时的整数对象并不是一个个独立的,而是相关联结成一个庞大的整数对象系统了。
小整数对象
在实际的编程中,数值比较小的整数,比如1,2,等等,这些在程序中是频繁使用到的,而Python中,所有的对象都存活在系统堆上,也就是说,如果没有特殊的机制,对于小整数对象,Python将一次次的malloc在堆上申请空间,然后free,这样的操作将大大降低了运行效率。
那么如何解决呢?Python中,对小整数对象使用了对象池技术。
那么又有一个问题了,Python中的大对象和小对象如何区分呢?嗯,Python中确实有一种方法,用户可以调整大整数和小整数的分界点,从而动态的确定小整数的对象池中应该有多少个小整数对象,但是调整的方法只有自己修改源代码,然后重新编译。
大整数对象
对于小整数,小整数对象池中完全的缓存PyIntObject对象,对于其它对象,Python将提供一块内存空间,这些内存空间将由这些大整数轮流使用,也就是谁需要的时候谁使用。
比如,在Python中有一个PyIntBlock结构,维护了一块内存,其中保存了一些PyIntObject对象,维护对象的个数也可以做动态的调整。在Python运行的某个时刻,有一些内存已经被使用,而另一些内存则处于空闲状态,而这些空闲的内存必须组织起来,那样,当Python需要新的内存时,才能快速的获得所需的内存,在Python中使用一个单向链表(free_list)来管理所有的空闲内存。
#define BLOCK_SIZE 1000 /* 1K less typical malloc overhead */
#define BHEAD_SIZE 8 /* Enough for a 64-bit pointer */
#define N_INTOBJECTS ((BLOCK_SIZE - BHEAD_SIZE) / sizeof(PyIntObject)) struct _intblock {
struct _intblock *next;
PyIntObject objects[N_INTOBJECTS];
}; typedef struct _intblock PyIntBlock; static PyIntBlock *block_list = NULL;
static PyIntObject *free_list = NULL;
创建
现在,我们已经大体知道Python中整数对象系统在内存是一种怎样的结构了,下面将介绍一个个PyIntObject是怎样的从无到有的产生。主要分为两步:
如果小整数对象池机制被激活,则尝试使用小整数对象池;如果不能使用小整数对象池,则使用通用整数对象池。
以PyInt_FromLong说明:
PyObject *
PyInt_FromLong(long ival)
{
register PyIntObject *v;
#if NSMALLNEGINTS + NSMALLPOSINTS > 0
if (-NSMALLNEGINTS <= ival && ival < NSMALLPOSINTS) {
v = small_ints[ival + NSMALLNEGINTS];
Py_INCREF(v);
#ifdef COUNT_ALLOCS
if (ival >= 0)
quick_int_allocs++;
else
quick_neg_int_allocs++;
#endif
return (PyObject *) v;
}
#endif
if (free_list == NULL) {
if ((free_list = fill_free_list()) == NULL)
return NULL;
}
/* Inline PyObject_New */
v = free_list;
free_list = (PyIntObject *)Py_TYPE(v);
PyObject_INIT(v, &PyInt_Type);
v->ob_ival = ival;
return (PyObject *) v;
}
Python源码分析(一)的更多相关文章
- Python源码分析(二) - List对象
python中的高级特性之一就是内置了list,dict等.今天就先围绕列表(List)进行源码分析. Python中的List对象(PyListObject) Python中的的PyListObje ...
- Python 源码分析:queue 队列模块
起步 queue 模块提供适用于多线程编程的先进先出(FIFO)数据结构.因为它是线程安全的,所以多个线程很轻松地使用同一个实例. 源码分析 先从初始化的函数来看: 从这初始化函数能得到哪些信息呢?首 ...
- Python源码分析之dis
一.简单例子 def add(a, b): return a + b add_nums.py import foo a = [1, 'python'] a = 'a string' def func( ...
- Python源码分析
- 分位数回归及其Python源码
分位数回归及其Python源码 天朗气清,惠风和畅.赋闲在家,正宜读书.前人文章,不得其解.代码开源,无人注释.你们不来,我行我上.废话少说,直入主题.o( ̄︶ ̄)o 我们要探测自变量 与因变量 的关 ...
- Python之美[从菜鸟到高手]--urlparse源码分析
urlparse是用来解析url格式的,url格式如下:protocol :// hostname[:port] / path / [;parameters][?query]#fragment,其中; ...
- [python] 基于词云的关键词提取:wordcloud的使用、源码分析、中文词云生成和代码重写
1. 词云简介 词云,又称文字云.标签云,是对文本数据中出现频率较高的“关键词”在视觉上的突出呈现,形成关键词的渲染形成类似云一样的彩色图片,从而一眼就可以领略文本数据的主要表达意思.常见于博客.微博 ...
- Python 进阶之源码分析:如何将一个类方法变为多个方法?
前一篇文章<Python 中如何实现参数化测试?>中,我提到了在 Python 中实现参数化测试的几个库,并留下一个问题: 它们是如何做到把一个方法变成多个方法,并且将每个方法与相应的参数 ...
- python基础-11 socket,IO多路复用,select伪造多线程,select读写分离。socketserver源码分析
Socket socket通常也称作"套接字",用于描述IP地址和端口,是一个通信链的句柄,应用程序通常通过"套接字"向网络发出请求或者应答网络请求. sock ...
随机推荐
- git开发错分支
1,代码未提交时: 使用以下命令即可解决. git add . (把所有改动暂存) git stash (把暂存的文件提交到git的暂存栈) git checkout 本该提交代码的 ...
- PHP返回32位与16位的md5加密值
字符串“123456”,经过md5算法加密之后是 32位: e10adc3949ba59abbe56e057f20f883e16位: 49ba59abbe56e057 PHP自带的 md5() 函数, ...
- torch 深度学习(5)
torch 深度学习(5) mnist torch siamese deep-learning 这篇文章主要是想使用torch学习并理解如何构建siamese network. siamese net ...
- 纯css做三角形形状
/* create an arrow that points up */ div.arrow-up { width:0px; height:0px; border-left:5px solid tra ...
- Ansible 小手册系列 五(inventory 主机清单)
Ansible 可同时操作属于一个组的多台主机,组和主机之间的关系通过 inventory 文件配置. 默认的文件路径为 /etc/ansible/hosts 主机清单示例 mail.example. ...
- JavaScript---forEach( ) 、map( )和 filter()
循环数组,最先想到的就是for循环: for(var i=0;i<count;i++) { //逻辑代码} 除此之外,就是forEach()方法了. Firefox 和Chrome 的Arra ...
- Java进阶2 数组内存和对象的内存管理知识
Java进阶2 数组内存和对象的内存管理知识 20131028 前言: 在面试的时候,如果是Java的编程语言,也许你认为没有什么可以问的,只能够说明你对于Java了解的太浅了,几乎就是两个星期的节奏 ...
- kvm虚拟机克隆注意点
1.硬盘空间会受第一次分配硬盘是的max capacity(最大容量) 限制,如果额外添加一块硬盘,会多出一个img文件,克隆这种虚拟机,两个img文件会都克隆下来,如果不重新命名会在原先img文件后 ...
- hdu4453
题解: splay模板 删除,翻转等等 代码: #include<cstdio> #include<cstring> #include<cmath> #includ ...
- python 获取当前时间(关于time()时间问题的重要补充)
python 获取当前时间 我有的时候写程序要用到当前时间,我就想用python去取当前的时间,虽然不是很难,但是老是忘记,用一次丢一次,为了能够更好的记住,我今天特意写下python 当前时间这 ...