美文网首页
Python 整数对象的实现

Python 整数对象的实现

作者: 超net | 来源:发表于2015-11-01 23:56 被阅读677次

    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 源码剖析》第二章:整数对象

    相关文章

      网友评论

          本文标题:Python 整数对象的实现

          本文链接:https://www.haomeiwen.com/subject/pllqhttx.html