[笔记]Python的整数对象:PyIntObject

原创 2012年03月10日 01:34:47
一般没有特指,参考的是Python 2.7.2的源码。

在intobject.h的开头就有英文注释,对PyIntObject进行了一下简单介绍。
原文如下:
/*
PyIntObject represents a (long) integer.  This is an immutable object;
an integer cannot change its value after creation.

There are functions to create new integer objects, to test an object
for integer-ness, and to get the integer value.  The latter functions
returns -1 and sets errno to EBADF if the object is not an PyIntObject.
None of the functions should be applied to nil objects.

The type PyIntObject is (unfortunately) exposed here so we can declare
_Py_TrueStruct and _Py_ZeroStruct in boolobject.h; don't use this.
*/
从注释可以看出以下几点:
1. PyIntObject实际是long类型;
2. 不可变对象,即创建后不能改变它的值;
3. PyIntObject提供了若干函数;
关于第二点,可以参考如下代码:

所以,看上去是a改变了值,实际上a是引用了不同的整数对象。



注释下面就是PyIntObject的定义了。
typedef struct {
    PyObject_HEAD
    long ob_ival;
} PyIntObject;


所以展开来的话,主要就包含了引用计数、对象类型和表示值的long ob_ival。

每一种类型有独有的特性和操作,对象的很多信息都放在对应的类型中,比如PyIntObject很多信息放在PyInt_Type中。
PyInt_Type定义在intobject.c中:
PyTypeObject PyInt_Type = {
    PyVarObject_HEAD_INIT(&PyType_Type, 0)
    "int",
    sizeof(PyIntObject),
    0,
    (destructor)int_dealloc,                    /* tp_dealloc */
    (printfunc)int_print,                       /* tp_print */
    0,                                          /* tp_getattr */
    0,                                          /* tp_setattr */
    (cmpfunc)int_compare,                       /* tp_compare */
    (reprfunc)int_to_decimal_string,            /* tp_repr */
    &int_as_number,                             /* tp_as_number */
    0,                                          /* tp_as_sequence */
    0,                                          /* tp_as_mapping */
    (hashfunc)int_hash,                         /* tp_hash */
    0,                                          /* tp_call */
    (reprfunc)int_to_decimal_string,            /* tp_str */
    PyObject_GenericGetAttr,                    /* tp_getattro */
    0,                                          /* tp_setattro */
    0,                                          /* tp_as_buffer */
    Py_TPFLAGS_DEFAULT | Py_TPFLAGS_CHECKTYPES |
        Py_TPFLAGS_BASETYPE | Py_TPFLAGS_INT_SUBCLASS,          /* tp_flags */
    int_doc,                                    /* tp_doc */
    0,                                          /* tp_traverse */
    0,                                          /* tp_clear */
    0,                                          /* tp_richcompare */
    0,                                          /* tp_weaklistoffset */
    0,                                          /* tp_iter */
    0,                                          /* tp_iternext */
    int_methods,                                /* tp_methods */
    0,                                          /* tp_members */
    int_getset,                                 /* tp_getset */
    0,                                          /* tp_base */
    0,                                          /* tp_dict */
    0,                                          /* tp_descr_get */
    0,                                          /* tp_descr_set */
    0,                                          /* tp_dictoffset */
    0,                                          /* tp_init */
    0,                                          /* tp_alloc */
    int_new,                                    /* tp_new */
    (freefunc)int_free,                         /* tp_free */
};


所以前面几项宏展开来是:
PyTypeObject PyInt_Type = {
     _PyObject_EXTRA_INIT
     1,                                   //引用计数
     PyType_Type,                    //对象类型
     0,                                   //可变部分中items的数目,PyIntObject为不可变对象
     "int",                              //类型名称
     sizeof(PyIntObject),          //该类型的基本大小
     0,                                   //类型包含的item的大小
     ......
}


后面的信息就是一些类型拥有的操作,显而易见的有创建对象和删除对象、输出、比较、整数操作和成员函数。
比如int_as_numbers的定义如下:
static PyNumberMethods int_as_number = {
    (binaryfunc)int_add,        /*nb_add*/
    (binaryfunc)int_sub,        /*nb_subtract*/
    (binaryfunc)int_mul,        /*nb_multiply*/
    (binaryfunc)int_classic_div, /*nb_divide*/
    (binaryfunc)int_mod,        /*nb_remainder*/
    (binaryfunc)int_divmod,     /*nb_divmod*/
    (ternaryfunc)int_pow,       /*nb_power*/
    (unaryfunc)int_neg,         /*nb_negative*/
    (unaryfunc)int_int,         /*nb_positive*/
    (unaryfunc)int_abs,         /*nb_absolute*/
    (inquiry)int_nonzero,       /*nb_nonzero*/
    (unaryfunc)int_invert,      /*nb_invert*/
    (binaryfunc)int_lshift,     /*nb_lshift*/
    (binaryfunc)int_rshift,     /*nb_rshift*/
    (binaryfunc)int_and,        /*nb_and*/
    (binaryfunc)int_xor,        /*nb_xor*/
    (binaryfunc)int_or,         /*nb_or*/
     ......
}


这里列出了一些基本运算,如加减乘除,还有位移、逻辑、幂运算等。
考虑一下加法运算过程。
static PyObject *
int_add(PyIntObject *v, PyIntObject *w)
{
    register long a, b, x;
    CONVERT_TO_LONG(v, a);
    CONVERT_TO_LONG(w, b);
    /* casts in the line below avoid undefined behaviour on overflow */
    x = (long)((unsigned long)a + b);
    if ((x^a) >= 0 || (x^b) >= 0)
        return PyInt_FromLong(x);
    return PyLong_Type.tp_as_number->nb_add((PyObject *)v, (PyObject *)w);
}


实际上是long类型的加法运算,并对运算结果进行判断是否溢出。
采取异或运算判断与0的比较结果,是为了判断符号位的变化。
考虑:正数+正数,负数+负数,正数+负数。
第一种情况下溢出,那么结果x将为负数,不满足if判断。
第二种情况下溢出,那么结果x将为正数,不满足if判断。
第三种情况,不会溢出,并且x的符号位必然和其中一个数相同,满足或的if判断。



最后是关于小整数和大整数的管理问题。
Python为小整数建立了内存池,小整数的范围可以自己定义(修改源码重新编译),因为小整数是使用十分频繁的对象。
而对于大整数,Python也采取了一定的措施。

先来看看创建一个整数对象的过程。
参考如下函数:
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;
}


该函数会对传进来的参数判断是否在小整数范围内(-NSMALLNEGINT ~ NSMALLPOSINTS),如果在就直接在small_ints数组里取。
     static PyIntObject *small_ints[NSMALLNEGINTS + NSMALLPOSINTS];
如果不是小整数,那么涉及的就是对大整数的处理。

在对大整数的处理中,首先涉及的是free_list,它被如下初始化:
     static PyIntObject *free_list = NULL;
所以,当第一次遇到大整数的时候,会调用函数fill_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;

static PyIntObject *
fill_free_list(void)
{
    PyIntObject *p, *q;
    /* Python's object allocator isn't appropriate for large blocks. */
    p = (PyIntObject *) PyMem_MALLOC(sizeof(PyIntBlock));
    if (p == NULL)
        return (PyIntObject *) PyErr_NoMemory();
    ((PyIntBlock *)p)->next = block_list;          //在成功分配内存后,将next指针指向上一个块(第一次为NULL)
    block_list = (PyIntBlock *)p;                    //让block_list始终指向最新的块
    /* Link the int objects together, from rear to front, then return
       the address of the last int object in the block. */
    p = &((PyIntBlock *)p)->objects[0];               //指针p指向数组中第一个元素
    q = p + N_INTOBJECTS;                              //指针q指向数组中最后一个元素的下一个(越界了)
    while (--q > p)                                   //利用(滥用)对象的ob_type指针来将所有元素链接在一起
        Py_TYPE(q) = (struct _typeobject *)(q-1);
    Py_TYPE(q) = NULL;                                   //最后一个(实际是第一个,因为是从最后向前遍历)的ob_type指针指向NULL
    return p + N_INTOBJECTS - 1;                         //返回数组的最后一个元素的地址
}


注释里面说明了Python的对象分配器不适合多个大内存块,所以一次获取的大小是sizeof(PyIntBlock)。
PyIntBlock里面包含了一个next指针和一个PyIntObject数组。
为了方便,将说明放在代码中进行注释。

接着回到PyInt_FromLong函数
{
     ......
    v = free_list;                              //v指向数组的最后一个元素,最新未使用的PyIntObject
    free_list = (PyIntObject *)Py_TYPE(v);      //free_list指向最新未使用的PyIntObject,目前未倒2个数组元素,注意,这里是用ob_type指针来链接的
    PyObject_INIT(v, &PyInt_Type);              //对v进行初始化,即刚得到的整数对象
    v->ob_ival = ival;                          //设置整数对象的值
    return (PyObject *) v;                      //返回整数对象
}




补充。
intobject.c开头的一段注释值得注意。
/* Integers are quite normal objects, to make object handling uniform.
   (Using odd pointers to represent integers would save much space
   but require extra checks for this special case throughout the code.)
   Since a typical Python program spends much of its time allocating
   and deallocating integers, these operations should be very fast.
   Therefore we use a dedicated allocation scheme with a much lower
   overhead (in space and time) than straight malloc(): a simple
   dedicated free list, filled when necessary with memory from malloc().

   block_list is a singly-linked list of all PyIntBlocks ever allocated,
   linked via their next members.  PyIntBlocks are never returned to the
   system before shutdown (PyInt_Fini).

   free_list is a singly-linked list of available PyIntObjects, linked
   via abuse of their ob_type members.
*/
这一段主要是说为了避免过多的内存分配开销,采用了特定的策略来管理整数对象的内存分配。
block_list是一个单向链表,保存着所有分配过的PyIntBlock,通过next指针链接,它会一直存在着,直到系统关闭(PyInt_Fini)。
free_list是PyIntObject的单向链表,通过对象的ob_type成员链接,这是忽略类型安全的“滥用”。
小整数也是通过这两个数据结构来管理的,只不过它是在一开始就初始化好,而大整数是运行时需要才创建的。

下面是大整数创建过程的草图。

Python内部机制-PyIntObject对象

Python int对象的实现Python内部关于int对象的实现在我之前的两篇文章中其实已经简单的介绍过,本文会前面的基础上更加深入的分析int对象的内部实现,以及Python对int对象进行优化而...

五、从PyIntObject出发

1.初识PyIntObject 本节要说的内容是“整数”,我们来看看这个PyInt_Type变量,这个变量里面大量的元信息是我们要关注的。它描述了一个整数对象。 [intobject.c] P...
  • debugm
  • debugm
  • 2012年11月30日 18:54
  • 1295

问题三十五: 怎么用ray tracing画二次曲面(quadratic surfaces)(4)——双曲抛物面(马鞍面)

35.4 双曲抛物面(马鞍面) 35.4.1 数学推导 双曲抛物面的方程如下: 35.4.2 看C++代码实现 ----------------------------------------...

python因为格式带来的问题

刚才在测试上篇文章中的makeTextFile.py。在运行的时候发现报IndentationError: unexpected indent错误。 python因为熟悉的{}被dict占用,所以...
  • crxmai
  • crxmai
  • 2013年11月20日 16:14
  • 1105

Python源码--整数对象(PyIntObject)的内存池

由于python中的整数对象记录的整数值是不可变的,所以在名字a的值不断变化的过程中,就就涉及到了多次对象的创建和销毁。所以python为整数对象申请空间进行了两种优化: 优化1:为通用整数对象存储池...
  • ordeder
  • ordeder
  • 2014年05月08日 23:29
  • 2851

[Python源码学习]之整数类型PyIntObject

在Python2中,有 PyIntObject 和 PyLongObject 两种整数类型,在Python3中,前者并入后者。本文是Python2 相关的内容。 PyIntObject 整...

Python源码学习笔记 2 整数对象

Python中的整数类型是不可变对象,为了提高python运行效率,内部实现了小整数对象池(数组实现),和普通整数缓冲池(单链表实现)。1.PyIntObject 该结构仅适用2.5版本,该版本下...
  • openex
  • openex
  • 2017年04月11日 00:13
  • 156

《python源码剖析》笔记 python中的整数对象

1. PyIntObject --> long的一个简单包装 typedef struct{ PyObject_HEAD long ob_ival; } PyIntObject; PyInt...
  • zhsenl
  • zhsenl
  • 2014年06月13日 17:56
  • 769

Python源码剖析笔记2-Python整数对象

Python源码剖析笔记2-Python整数对象本文简书地址: http://www.jianshu.com/p/0136ed90cd46 千里之行始于足下,从简单的类别开始分析,由浅入深也不至于...

深入 Python 整数对象的实现

本文由 伯乐在线 - Namco 翻译,wrm 校稿。未经许可,禁止转载! 英文出处:Laurent Luce。欢迎加入翻译组。 http://python.jobbole.com/82632/ ...
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:[笔记]Python的整数对象:PyIntObject
举报原因:
原因补充:

(最多只允许输入30个字)