对Python list的认识

python中对list的定义如下:

typedef struct {
    PyObject_VAR_HEAD
    PyObject **ob_item;
    Py_ssize_t allocated;
} PyListObject;

看定义非常简单哈~PyObject_VAR_HEAD说明list是一个变长对象,要注意的是,list同时也是一个可变对象。(前面说过python里面string也是变长对象,但是string是一个不可变的对象)。ob_item是指向list中存的对象的指针,这个等会细讲。allocated是指list可容纳的元素的总数。那如果之前的博客读得认真的话,就是知道,Python_VAR_HEAD里面也有一个变量是ob_size,也是说list里面存多少个对象。那么两者有冲突么?聪明的读者估计能猜到了,Python里面的list和C++里面的vector非常相似,采用了类似的内存管理策略,相应于C++ vector的size 和capacity,ob_size指的是目前list中存了多少元素,而allocated则是说如果不重新分配内存的话,最多可以存多少个元素。例如:L= [1,2,3,4],那么ob_size是4,而allocated可能是6,也可能是7,但是貌似不可能是10,如果ob_size小于allocated的一般,python一般对进行reallocate来节省内存(Python对会到处注意要节省内存)。0<=ob_size<=allocated, len(L) = 4。


来看一下python list是如何被创建的:

PyObject *
PyList_New(Py_ssize_t size)
{
    PyListObject *op;
    size_t nbytes;
    。。。。。。。// 省略,判断内存容量是否超标
    nbytes = size * sizeof(PyObject *);
    if (numfree) {
        numfree--;
       op = free_list[numfree];//重点
        _Py_NewReference((PyObject *)op);
#ifdef SHOW_ALLOC_COUNT
        count_reuse++;
#endif
    } else {
        op = PyObject_GC_New(PyListObject, &PyList_Type);//重点
        if (op == NULL)
            return NULL;
#ifdef SHOW_ALLOC_COUNT
        count_alloc++;
#endif
    }
    if (size <= 0)
        op->ob_item = NULL;
    else {
        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;
}


重点我已经用“//重点”出来了(因为加粗和加颜色貌似都不管用 T T),函数一开始会进行判断,看要分配的内存有没有超过内存总量。在没有超标的情况下,大致分两步,第一步是创建PyListObject,python首先会去看看对象池中有没有可用的PylistObject,还有可用的话就直接拿来用,没有了的话那就再new一个出来。第二步是给PylistObject要存储的对象分配内存,然后再去给ob_item赋值。

这样就要涉及到关于list的对象池问题了,用free_list来存储,不同于int和string, python中list的对象池是在list对象deallocate的时候被填充的。


static void
list_dealloc(PyListObject *op)
{
    ......//省略释放所指元素的代码
    if (numfree < PyList_MAXFREELIST && PyList_CheckExact(op))
        free_list[numfree++] = op;
    else
        Py_TYPE(op)->tp_free((PyObject *)op);
    Py_TRASHCAN_SAFE_END(op)
}

可以清楚的看到,在deallocate的时候,如果对象池没满的话(定义是80个PylistObject,可以自己修改编译),就存到对象池里面,满了的话就释放掉相应的内存空间。


现在来看看ob_item,它指向的是一个数组,数组里面存的是指向真正list元素的指针。如果用PyList_new(6)来创建一个对象,并且把第四项设置为100的话,大概会如下图所示:

对Python list的认识_第1张图片

这是书里面的截图,当然前面几个元素不可能是NULL啦,这里只是演示用而已。

我个人觉得这里直接写100不是很正确,这里面应该也是一个指针,指向的是100这个元素。不知道我的理解对不对?

从下面set_item的带注释代码中可以看到:

int
PyList_SetItem(register PyObject *op, register Py_ssize_t i,
               register PyObject *newitem)
{
    register PyObject *olditem;
    register PyObject **p;
    .......//省略
    p = ((PyListObject *)op) -> ob_item + i;
    olditem = *p;
    *p = newitem;//所以100其实应该是指向100的指针???
    Py_XDECREF(olditem);
    return 0;
}


差不多对python list的认识就是这些,有错的请指出哦~

你可能感兴趣的:(对Python list的认识)