《Python源码剖析》读书笔记-2 整数对象


第2章 Python中的整数对象

  • 整数对象定义为
    1
    2
    3
    4
    typedef struct {
    PyObject_HEAD
    long ob_ival;
    } PyIntObject;
  • tp_dealloc(在PyInt_Type中是int_dealloc)和tp_free(在PyInt_Type中是int_free)的区别:在整数对象这里确实没啥区别。只是对外暴露的接口是tp_dealloc,而tp_free是供tp_dealloc调用的。在PyString_Type中则看的更明白——当一个字符串的引用计数为0时,tp_dealloc会被调用,在其中处理一些关于缓存的事务后,最后靠tp_free具体释放内存。
  • tp_as_number(在PyInt_Type中是int_as_number),定义了关于数字运算的一族函数。以int_add为例:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    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);
    }
    • CONVERT_TO_LONG提取出了PyIntObject中的ob_ival部分,其实现中,使用PyInt_Check宏检查对象是否是Int类型。检查时使用了PyInt_Type此类型对象的tp_flags域。
    • 当加法结果(和)与两个加法参数(加数)符号相反时,就代表出现了溢出。这很容易理解,溢出一般就是两个负数相加出现了正数 或者两个正数相加出现了负数,而一正一负的两个数相加永远不会溢出。溢出时,这里自动将其表示范围扩大,当做long类型处理,所以在python里,就感觉不到int类型的加法溢出了。
    • Int类型是不可变类型,两个int对象相加,最后会产生一个新的对象(int或者long)
  • ###整数对象缓存

    • 定义一个small_ints的PyIntObject数组,保存[-NSMALLNEGINTS, NSMALLPOSINTS)的整数对象。因此,在C源码中经常可以看见这样的if语句:

      1
      if (-NSMALLNEGINTS <= ival && ival < NSMALLPOSINTS)
    • 如果满足条件,就会从small_ints数组中取出对应的PyIntObject使用。

    • 通用整数对象池不会缓存所有的整数,只会缓存其中的一部分,其设计类似于STL中Vector的内存管理。
    • 此对象池的每一个block中都缓存了大约82个(可以自行设置)PyIntObject对象(数组形式),以链表形式存储,链表顺序从数组的最后一个元素开始,到第一个元素结束。
    • 所有的block也以链表形式存储。
    • 构造链表时,使用了PyIntObject的ob_type指针作为链表指针。这是为了节省内存,同时这里的数据类型都是Int,也就可以这样做了。
    • 当当前block中的82个PyIntObject内存使用完时,就新申请一个block,并且将其加进block链表,同时free_list指针指向新的block中PyIntObject数组的最后一个。
    • 当一个整数对象的引用计数减小为0时,会调用int_deallloc函数,这个函数会把这块内存接到free_list所代表的空闲内存块中。
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      static void
      int_dealloc(PyIntObject *v)
      {

      if (PyInt_CheckExact(v)) {
      Py_TYPE(v) = (struct _typeobject *)free_list;
      free_list = v;
      }
      else
      Py_TYPE(v)->tp_free((PyObject *)v);
      }

评论