Python整数对象实现原理详解


Posted in Python onJuly 01, 2019

整数对象在Python内部用PyIntObject结构体表示:

typedef struct {
PyObject_HEAD
long ob_ival;
} PyIntObject;

PyObject_HEAD宏中定义的两个属性分别是:

int ob_refcnt; 
struct _typeobject *ob_type;

这两个属性是所有Python对象固有的:

  • ob_refcnt:对象的引用计数,与Python的内存管理机制有关,它实现了基于引用计数的垃圾收集机制
  • ob_type:用于描述Python对象的类型信息。

由此看来PyIntObject就是一个对C语言中long类型的数值的扩展,出于性能考虑,对于小整数,Python使用小整数对象池small_ints缓存了[-5,257)之间的整数,该范围内的整数在Python系统中是共享的。

#define NSMALLPOSINTS 257
#define NSMALLNEGINTS 5
static PyIntObject *small_ints[NSMALLNEGINTS + NSMALLPOSINTS];

Python整数对象实现原理详解

而超过该范围的整数即使值相同,但对象不一定是同一个,如下所示:当a与b的值都是10000,但并不是同一个对象,而值为1的时候,a和b属于同一个对象。

>>> a = 10000
>>> b = 10000
>>> print a is b
False
>>> a = 1
>>> b = 1
>>> print a is b
True

对于超出了[-5, 257)之间的其他整数,Python同样提供了专门的缓冲池,供这些所谓的大整数使用,避免每次使用的时候都要不断的malloc分配内存带来的效率损耗。这块内存空间就是PyIntBlock。

struct _intblock {
struct _intblock *next;
PyIntObject objects[N_INTOBJECTS];
};
typedef struct _intblock PyIntBlock;
static PyIntBlock *block_list = NULL;
static PyIntObject *free_list = NULL;

这些内存块(PyIntBlock)通过一个单向链表组织在一起,表头是block_list,表头始终指向最新创建的PyIntBlock对象。

PyIntBlock有两个属性:next,objects。next指针指向下一个PyIntBlock对象,objects是一个PyIntObject数组(最终会转变成单向链表),它是真正用于存储被缓存的PyIntObjet对象的内存空间。

free_list单向链表是所有PyIntBlock内存块中空闲的内存。所有空闲内存通过一个链表组织起来的好处就是在Python需要新的内存来存储新的PyIntObject对象时,能够通过free_list快速获得所需的内存。

Python整数对象实现原理详解

创建一个整数对象时,如果它在小整数范围内,就直接从小整数缓冲池中直接返回,如果不在该范围内,就开辟一个大整数缓冲池内存空间:

[intobject.c]
PyObject* PyInt_FromLong(long ival)
{
register PyIntObject *v; 
#if NSMALLNEGINTS + NSMALLPOSINTS > 0
//[1] :尝试使用小整数对象池
if (-NSMALLNEGINTS <= ival && ival < NSMALLPOSINTS) {
v = small_ints[ival + NSMALLNEGINTS];
Py_INCREF(v);
return (PyObject *) v;
}
#endif
//[2] :为通用整数对象池申请新的内存空间
if (free_list == NULL) {
if ((free_list = fill_free_list()) == NULL)
return NULL;
}
//[3] : (inline)内联PyObject_New的行为
v = free_list;
free_list = (PyIntObject *)v->ob_type;
PyObject_INIT(v, &PyInt_Type);
v->ob_ival = ival;
return (PyObject *) v;
}

fill_free_list就是创建大整数缓冲池内存空间的逻辑,该函数返回一个free_list链表,当整数对象ival创建成功后,free_list表头就指向了v->ob_type,ob_type不是所有Python对象中表示类型信息的字段吗?怎么在这里作为一个连接指针呢?这是Python在性能与代码优雅之间取中庸之道,对名称的滥用,放弃了对类型安全的坚持。把它理解成指向下一个PyIntObject的指针即可。

[intobject.c]
static PyIntObject* fill_free_list(void)
{
PyIntObject *p, *q;
// 申请大小为sizeof(PyIntBlock)的内存空间
// block list始终指向最新创建的PyIntBlock
p = (PyIntObject *) PyMem_MALLOC(sizeof(PyIntBlock));
((PyIntBlock *)p)->next = block_list;
block_list = (PyIntBlock *)p;
//:将PyIntBlock中的PyIntObject数组(objects)转变成单向链表
p = &((PyIntBlock *)p)->objects[0];
q = p + N_INTOBJECTS;
while (--q > p)
// ob_type指向下一个未被使用的PyIntObject。
q->ob_type = (struct _typeobject *)(q-1);
q->ob_type = NULL;
return p + N_INTOBJECTS - 1;
}

不同的PyIntBlock里面的空闲的内存是怎样连接起来构成free_list的呢?这个秘密放在了整数对象垃圾回收的时候,在PyIntObject对象的tp_dealloc操作中可以看到:

[intobject.c]
static void int_dealloc(PyIntObject *v)
{
if (PyInt_CheckExact(v)) {
v->ob_type = (struct _typeobject *)free_list;
free_list = v;
}
else
v->ob_type->tp_free((PyObject *)v);
}

原来PyIntObject对象销毁时,它所占用的内存并不会释放,而是继续被Python使用,进而将free_list表头指向了这个要被销毁的对象上。

总结

  • Python中的int对象就是c语言中long类型数值的扩展
  • 小整数对象[-5, 257]在python中是共享的
  • 整数对象都是从缓冲池中获取的。
  • 整数对象回收时,内存并不会归还给系统,而是将其对象的ob_type指向free_list,供新创建的整数对象使用

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Python 相关文章推荐
Python中处理时间的几种方法小结
Apr 09 Python
深入解析Python中的WSGI接口
May 11 Python
python 判断是否为正小数和正整数的实例
Jul 23 Python
解决Python字典写入文件出行首行有空格的问题
Sep 27 Python
django使用xlwt导出excel文件实例代码
Feb 06 Python
详解TensorFlow查看ckpt中变量的几种方法
Jun 19 Python
Windows下Python3.6安装第三方模块的方法
Nov 22 Python
TensorFlow梯度求解tf.gradients实例
Feb 04 Python
keras tensorflow 实现在python下多进程运行
Feb 06 Python
python 遗传算法求函数极值的实现代码
Feb 11 Python
keras Lambda自定义层实现数据的切片方式,Lambda传参数
Jun 11 Python
python两个list[]相加的实现方法
Sep 23 Python
python实现两个dict合并与计算操作示例
Jul 01 #Python
Python字符串对象实现原理详解
Jul 01 #Python
Python转换时间的图文方法
Jul 01 #Python
Python列表对象实现原理详解
Jul 01 #Python
win8.1安装Python 2.7版环境图文详解
Jul 01 #Python
Python为何不能用可变对象作为默认参数的值
Jul 01 #Python
浅析Python与Mongodb数据库之间的操作方法
Jul 01 #Python
You might like
浅析PHP递归函数返回值使用方法
2013/02/18 PHP
PHP zip扩展Linux下安装过程分享
2014/05/05 PHP
jquery获得页面元素的坐标值实现思路及代码
2013/04/15 Javascript
JavaScript生成福利彩票双色球号码
2015/05/15 Javascript
javascript实现简单查找与替换的方法
2015/07/22 Javascript
黑帽seo劫持程序,js劫持搜索引擎代码
2015/09/15 Javascript
浅谈JavaScript前端开发的MVC结构与MVVM结构
2016/06/03 Javascript
javascript 解决浏览器不支持的问题
2016/09/24 Javascript
vue.js将unix时间戳转换为自定义时间格式
2017/01/03 Javascript
微信小程序 二维码canvas绘制实例详解
2017/01/06 Javascript
js实现自定义路由
2017/02/04 Javascript
JavaScript纯色二维码变成彩色二维码
2020/07/23 Javascript
vue-router 路由基础的详解
2017/10/17 Javascript
React Native使用百度Echarts显示图表的示例代码
2017/11/07 Javascript
vue轮播图插件vue-awesome-swiper
2017/11/27 Javascript
vue toggle做一个点击切换class(实例讲解)
2018/03/13 Javascript
JavaScript设计模式之单例模式简单实例教程
2018/07/02 Javascript
nodejs中express入门和基础知识点学习
2018/09/13 NodeJs
记React connect的几种写法(小结)
2018/09/18 Javascript
Python中数字以及算数运算符的相关使用
2015/10/12 Python
Python编程之string相关操作实例详解
2017/07/22 Python
python中的常量和变量代码详解
2018/07/25 Python
Python 通过调用接口获取公交信息的实例
2018/12/17 Python
python 将日期戳(五位数时间)转换为标准时间
2019/07/11 Python
使用Python完成15位18位身份证的互转功能
2019/11/06 Python
python+gdal+遥感图像拼接(mosaic)的实例
2020/03/10 Python
Python-jenkins模块之folder相关操作介绍
2020/05/12 Python
纯HTML5+CSS3制作图片旋转
2016/01/12 HTML / CSS
100%法国制造的游戏和玩具:Les Jouets Français
2021/03/02 全球购物
给老师的道歉信
2014/01/11 职场文书
十佳大学生事迹材料
2014/01/29 职场文书
计算机科学系职业生涯规划书
2014/03/08 职场文书
幼儿园八一建军节活动方案
2014/08/27 职场文书
大学生考试作弊检讨书1000字
2014/10/14 职场文书
pandas中pd.groupby()的用法详解
2022/06/16 Python
MutationObserver在页面水印实现起到的作用详解
2022/07/07 Javascript