C27615e21379f7124bbe12e39f66db57
Python的数组实现细节

Python的数组实现细节

上文Python 的数组实现,我们探讨了PyListObject的数据结构以及对应的迭代器的一些设计思路。

按照惯例,我们这篇文章会继续深挖PyListObject一些巧妙的设计之处,看看Python作者究竟对数组对象进行了何种优化。

PyListObject对象的创建

我们的切入点还是选择从对象的创建入手,从[listobject.h]来看,Python对数组对象的创建只提供了一种了方式:

PyAPI_FUNC(PyObject *) PyList_New(Py_ssize_t size);

注意,这里的size,指的是对象指针的个数。

这个函数的实现具体如下:

PyObject *
PyList_New(Py_ssize_t size)
{
    PyListObject *op;
    size_t nbytes;

    if (size < 0) {
        PyErr_BadInternalCall();
        return NULL;
    }
    /* Check for overflow without an actual overflow,
     *  which can cause compiler to optimise out */
    if ((size_t)size > PY_SIZE_MAX / sizeof(PyObject *))
        return PyErr_NoMemory();
    nbytes = size * sizeof(PyObject *);
    if (numfree) {
        numfree--;
        op = free_list[numfree];
        _Py_NewReference((PyObject *)op);
    } else {
        op = PyObject_GC_New(PyListObject, &PyList_Type);
        if (op == NULL)
            return NULL;
    }
    if (size <= 0)
        op->ob_item = NULL;
    else {
        op->ob_item = (PyObject **) PyMem_MALLOC(nbytes);
        if (op->ob_item == NULL) {
            Py_DECREF(op);
            return PyErr_NoMemory();
        }
        memset(op->ob_item, 0, nbytes);
    }
    Py_SIZE(op) = size;
    op->allocated = size;
    _PyObject_GC_TRACK(op);
    return (PyObject *) op;
}

这里的代码可以分为几部分:

  • 检查参数的异常情况,比如负数、溢出等等。
  • 计算正确的分配大小(参数只是个数,而ob_item维护的一组对象指针)
  • 看看是否有循环复用的空间。(这块相比大家已经很熟悉了,在之前的PyIntObjectPyFloatObject)都有提及。

整体来看并没有什么特殊的地方,唯一需要关注的是,我们并没有看到类似之前Python 整数对象的内存管理_PyInt_Init这个用于初始化全局整数对象缓存池的步骤,那么PyListObject缓存池在哪呢?

一开始我也懵逼了半天,直到我按照之前的方式,找了相关的free/dealloc函数,才发现PyListObject的函数是一种类似懒加载的方式。

我们一起来看看list_dealloc函数:

static void
list_dealloc(PyListObject *op)
{
    Py_ssize_t i;
    if (op->ob_item != NULL) {
        /* Do it backwards, for Christian Tismer.
           There's a simple test case where somehow this reduces
           thrashing when a *very* large list is created and
           immediately deleted. */
        i = Py_SIZE(op);
        while (--i >= 0) {
            Py_XDECREF(op->ob_item[i]);
        }
        PyMem_FREE(op->ob_item);
    }

    // 关键点!!!!!!!!
top Created with Sketch.