python的List对象

Python的List对象在内存管理上采用了一次性申请大块内存的策略,以减少频繁申请带来的开销。文章详细介绍了PyListObject的ob_size和allocated字段的关系,以及创建、销毁、设置、插入和删除元素的过程。创建时利用缓冲池提高效率,销毁时对象可能返回缓冲池。插入元素时,根据需要动态调整内存,删除则通过比较并调用list_ass_slice实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

PyListObject对象

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

ob_itemc是指向元素列表的一个指针,在PyListObject对象中,有一个allocated字段,那么这个字段与PyObject_VAR_HEAD中的ob_size字段有什么关系呢?实际上,allocated指的是当前对象可容纳的元素的个数,而ob_size指的是当前对象有存储了多少个元素,由此可以得出,PyListObject对象在申请空间的时候并不是存一次申请一次,而是一次性申请一块很大的内存,避免每次操作的时候都要去申请内存。
创建PyListObject对象

PyObject *PyList_New(Py_ssize_t size)
{
    PyListObject *op;
    if (size < 0) {
        PyErr_BadInternalCall();
        return NULL;
    }
    if (numfree) {
        numfree--;
        op = free_list[numfree];
        _Py_NewReference((PyObject *)op);
    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_Calloc(size, sizeof(PyObject *));
        if (op->ob_item == NULL) {
            Py_DECREF(op);
            return PyErr_NoMemory();
        }
    }
    Py_SIZE(op) = size;
    op->allocated = size;
    _PyObject_GC_TRACK(op);
    return (PyObject *) op;
}

这个是python创建一个PyListObject时所执行的方法。python需要申请两次内存,一次是为PyListObject申请内存,另一次是为元素列表申请内存。在创建PyListObject对象之前,python会先检查缓冲池(缓冲池机制开启)free_list中是否有可用的对象,如果有,则直接使用这个对象,如果没有,就会通过 PyObject_GC_New在系统堆中申请内存。free_list默认最多会存储80个对象

#define PyList_MAXFREELIST 80
static PyListObject *free_list[PyList_MAXFREELIST];

缓冲池中的对象是从哪里来的?

static void
list_dealloc(PyListObject *op)
{
    Py_ssize_t i;
    PyObject_GC_UnTrack(op);
    Py_TRASHCAN_SAFE_BEGIN(op)
    if (op->ob_item != NULL) {
        i = Py_SIZE(op);
        while (--i >= 0) {
            Py_XDECREF(op->ob_item[i]);
        }
        PyMem_FREE(op->ob_item);
    }
    if (numfree < PyList_MAXFREELIST && PyList_CheckExact(op))
        free_list[numfree++] = op;
    else
        Py_TYPE(op)->tp_free((PyObject *)op);
    Py_TRASHCAN_SAFE_END(op)
}

PyListObject销毁时执行的方法。python会先检查缓冲池中的数量是否已经满了,如果没有就将该待删除的PyListObject放入缓冲池中,以便下次使用。
设置元素

int PyList_SetItem(PyObject *op, Py_ssize_t i,
               PyObject *newitem)
{
    PyObject **p;
    if (!PyList_Check(op)) {
        Py_XDECREF(newitem);
        PyErr_BadInternalCall();
        return -1;
    }
    if (i < 0 || i >= Py_SIZE(op)) {
        Py_XDECREF(newitem);
        PyErr_SetString(PyExc_IndexError,
                        "list assignment index out of range");
        return -1;
    }
    p = ((PyListObject *)op) -> ob_item + i;
    Py_XSETREF(*p, newitem);
    return 0;
}

#define Py_XSETREF(op, op2)                     \
    do {                                        \
        PyObject *_py_tmp = (PyObject *)(op);   \
        (op) = (op2);                           \
        Py_XDECREF(_py_tmp);                    \
    } while (0)

在设置元素的时候,python首先会进行类型的检查,然后再进行索引的有效性检查。都通过之后,python会将待加入的PyObject*指针放到指定位置,然后通过调整引用计数,将这个位置原来存放的对象的引用-1。
插入元素

int PyList_Insert(PyObject *op, Py_ssize_t where, PyObject *newitem)
{
    if (!PyList_Check(op)) {
        PyErr_BadInternalCall();
        return -1;
    }
    return ins1((PyListObject *)op, where, newitem);
}

static int ins1(PyListObject *self, Py_ssize_t where, PyObject *v)
{
    Py_ssize_t i, n = Py_SIZE(self);
    PyObject **items;
    if (v == NULL) {
        PyErr_BadInternalCall();
        return -1;
    }
    if (n == PY_SSIZE_T_MAX) {
        PyErr_SetString(PyExc_OverflowError,
            "cannot add more objects to list");
        return -1;
    }

    if (list_resize(self, n+1) < 0)
        return -1;

    if (where < 0) {
        where += n;
        if (where < 0)
            where = 0;
    }
    if (where > n)
        where = n;
    items = self->ob_item;
    for (i = n; --i >= where; )
        items[i+1] = items[i];
    Py_INCREF(v);
    items[where] = v;
    return 0;
}

python内部调用了PyList_Insert来完成元素的插入动作,而PyList_Insert实际上时调用了Python内部的ins1。在插入元素之前,必须要保证PyListObject对象必须有足够的内存来容纳要插入的元素。在调整PyListObject对象所维护的列表的内存时,python分为了两种情况进行处理。

allocated >= newsize && newsize >= (allocated >> 1)时,只简单调整ob_size的值,并没有改变空间的大小
其他情况,调用realloc,重新分配空间,new_allocated = (size_t)newsize + (newsize >> 3) + (newsize < 9 ? 3 : 6);

python会根据当前元素的个数,动态调整增加的空间的大小。
删除元素

static PyObject *list_remove(PyListObject *self, PyObject *value)
{
    Py_ssize_t i;

    for (i = 0; i < Py_SIZE(self); i++) {
        int cmp = PyObject_RichCompareBool(self->ob_item[i], value, Py_EQ);
        if (cmp > 0) {
            if (list_ass_slice(self, i, i+1,
                               (PyObject *)NULL) == 0)
                Py_RETURN_NONE;
            return NULL;
        }
        else if (cmp < 0)
            return NULL;
    }
    PyErr_SetString(PyExc_ValueError, "list.remove(x): x not in list");
    return NULL;
}

python会对PyListObject中的没有个元素一一进行比较
,一旦查找到匹配的元素,就会立即调用list_ass_slice方法删除该元素。实际上该方法并不是用于删除操作的方法。它的完整功能如下:

a[ilow: ihith] = v if v != NULL
del a[ilow: ihigh] if v == NULL

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值