Python 整数对象的实现

Python 的内建对象存放在源代码的Objects目录下。
intobject.c用于整数对象

在 Python 中,整数分为小整数对象大整数对象

小整数对象

由于数值较小的整数对象在内存中会很频繁地使用,如果每次都向内存申请空间、请求释放,会严重影响 Python 的性能。好在 整数对象 属于不可变对象,可以被共享而不会被修改导致问题,所以为 小整数对象 划定一个范围,即小整数对象池,在Python运行时初始化并创建范围内的所有整数,这个范围内的 整数对象是被共享的,即一次创建,多次共享引用。

那么这个范围是多少呢?从源文件中可以看到,而且,用户可以自行调整,只是每次都要在源文件中修改,而后进行编译、安装。

小整数池的范围:

#ifndef NSMALLPOSINTS
#define NSMALLPOSINTS           257
#endif
#ifndef NSMALLNEGINTS
#define NSMALLNEGINTS           5
#endif
#if NSMALLNEGINTS + NSMALLPOSINTS > 0
/* References to small integers are saved in this array so that they
   can be shared.
   The integers that are saved are those in the range
   -NSMALLNEGINTS (inclusive) to NSMALLPOSINTS (not inclusive).
*/
static PyIntObject *small_ints[NSMALLNEGINTS + NSMALLPOSINTS];

从源代码可以看出
define NSMALLPOSINTS 257,范围的右边界
define NSMALLNEGINTS 5,范围的左边界
-NSMALLNEGINTS (inclusive) to NSMALLPOSINTS (not inclusive),[-5, 257)

大整数对象

但是,整数对象很多,不一定都是小整数对象,又不能将所有整数对象都放入内存。于是,Python 提供了一个可扩展的内存空间,称为通用整数对象池,谁需要用就给谁用,这样免去了申请空间,又能提高一些效率。

这个空间是一个PyIntBlock结构,是用一个单向列表连接一串内存(block),这个列表由block_list维护,而每个 block 维护一个 整数对象数组(Objects),用于存放被缓存的整数对象。block_list的内容是最新创建的 block。
小整数对象池 也在block_list上。

Python 使用一个单向链表管理全部 block 的 objects 中的所有空闲内存,由free_list指出下一个可用的空闲内存。如果当前没有空闲内存,free_listNULL,会创建新的内存。

当整数对象的引用计数变为0,会销毁对象,但并不会释放空闲出来的内存,即将内存交还系统,而是重新加入free_list

hack

使用Xcode修改打印整数对象的方法

原始文件

/* ARGSUSED */
static int
int_print(PyIntObject *v, FILE *fp, int flags)
     /* flags -- not used but required by interface */
{
    long int_val = v->ob_ival;
    Py_BEGIN_ALLOW_THREADS
    fprintf(fp, "%ld", int_val);
    Py_END_ALLOW_THREADS
    return 0;
}

修改后,可以打印部分小整数地址池中,整数对象的引用次数、所在内存地址、下一个可用的空闲内存地址

/* ARGSUSED */

static int values[10];
static int refcounts[10];

static int
int_print(PyIntObject *v, FILE *fp, int flags)
     /* flags -- not used but required by interface */
{
    PyIntObject* intObjectPtr;
    PyIntBlock *p = block_list;
    PyIntBlock *last = NULL;
    int count = 0;
    int i;
    
    while(p != NULL)
    {
        ++count;
        last = p;
        p = p->next;
    }
    
    intObjectPtr = last->objects;
    intObjectPtr += N_INTOBJECTS - 1;
    printf(" address @%p\n", v);
    
    for(i = 0; i < 10; ++i, -- intObjectPtr)
    {
        values[i] = intObjectPtr -> ob_ival;
        refcounts[i] = intObjectPtr -> ob_refcnt;
    }
    printf(" value : ");
    for(i = 0; i < 8; ++i)
    {
        printf("%d\t", values[i]);
    }
    printf("\n");
    
    printf(" refcnt : ");
    for(i = 0; i < 8; ++i)
    {
        printf("%d\t", refcounts[i]);
    }
    printf("\n");
    
    printf(" block_list count : %d\n", count);
    printf(" free_list : %p\n", free_list);
    
    return 0;
/*    long int_val = v->ob_ival;
    Py_BEGIN_ALLOW_THREADS
    fprintf(fp, "%ld", int_val);
    Py_END_ALLOW_THREADS
    return 0;*/
}

保存并编译、安装,运行修改后的 Python

Python 2.6.9 (unknown, Nov  1 2015, 20:22:05)
[GCC 4.2.1 Compatible Apple LLVM 7.0.0 (clang-700.1.76)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>>
>>> i = -9999
>>> i
 address @0x7fe412f16470       # -9999 所在的内存地址
 value : -5 -4  -3  -2  -1  0   1   2   # 能够显示的 -5 ~ 2
 refcnt : 1 1   1   1   35  105 64  41     # 引用计数器,可以看到,小整数已经被Python自身使用了多次
 block_list count : 8       # block 数量
 free_list : 0x7fe412f16488     # 下一个可用的空闲内存地址

>>>
>>> a = -258
>>> a
 address @0x7fe412f16488     # -258 的内存地址,是上面`free_list`指出的空闲内存
 value : -5 -4  -3  -2  -1  0   1   2
 refcnt : 1 1   1   1   35  105 64  41
 block_list count : 8
 free_list : 0x7fe412f164a0    # 新的空闲内存地址

>>> b = -258
>>> b
 address @0x7fe412f164a0    # 上一个的空闲内存地址,可以看出,对于多次创建的大整数对象,即使值一样,也是不同的内存地址
 value : -5 -4  -3  -2  -1  0   1   2
 refcnt : 1 1   1   1   35  105 64  41
 block_list count : 8
 free_list : 0x7fe412f164b8

>>> del b               # 释放 b 的内存空间
>>> a
 address @0x7fe412f16488
 value : -5 -4  -3  -2  -1  0   1   2
 refcnt : 1 1   1   1   35  105 64  41
 block_list count : 8
 free_list : 0x7fe412f164a0    # 删除 b 后,新的空闲内存重新加入`free_list`,没有归还给系统

>>> c1 = -5          # 属于小整数对象池
>>> c1
 address @0x7fe412f033d8
 value : -5 -4  -3  -2  -1  0   1   2
 refcnt : 5 1   1   1   35  105 64  41   # -5 引用此时为 5
 block_list count : 8
 free_list : 0x7fe412f164b8

>>> c2 = -5        # 同上
>>> c2
 address @0x7fe412f033d8      # 两次创建的相同小整数对象,指向了相同的内存地址
 value : -5 -4  -3  -2  -1  0   1   2
 refcnt : 6 1   1   1   35  105 64  41     # -5 的引用次数加一,变为 6
 block_list count : 8
 free_list : 0x7fe412f164b8

>>>

整数对象的说明文件内置在源代码中:

PyDoc_STRVAR(int_doc,
"int(x[, base]) -> integer\n\
\n\
Convert a string or number to an integer, if possible.  A floating point\n\
argument will be truncated towards zero (this does not include a string\n\
representation of a floating point number!)  When converting a string, use\n\
the optional base.  It is an error to supply a base when converting a\n\
non-string.  If base is zero, the proper base is guessed based on the\n\
string content.  If the argument is outside the integer range a\n\
long object will be returned instead.");

int_doc就是整数对象的__doc__属性

参考资料

《Python 源码剖析》第二章:整数对象

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,324评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,356评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,328评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,147评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,160评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,115评论 1 296
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,025评论 3 417
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,867评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,307评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,528评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,688评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,409评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,001评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,657评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,811评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,685评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,573评论 2 353

推荐阅读更多精彩内容