Python listobject 列表对象


listobject

PyListObject

typedef struct {
    PyObject_VAR_HEAD
    PyObject **ob_item;
    Py_ssize_t allocated;
} PyListObject;
  • PyObject **ob_item;:列表元素指针存储的地方。
  • Py_ssize_t allocated:列表分配的空间,不是指元素的个数,元素的个数在PyObject_VAR_HEADob_size字段中。

PyList_New

初始化列表

PyObject *
PyList_New(Py_ssize_t size)
{
    if (size < 0) {
        PyErr_BadInternalCall();
        return NULL;
    }
    struct _Py_list_state *state = get_list_state();
    PyListObject *op;
#ifdef Py_DEBUG
    // PyList_New() must not be called after _PyList_Fini()
    assert(state->numfree != -1);
#endif
    if (state->numfree) {
        state->numfree--;
        op = state->free_list[state->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_Calloc(size, sizeof(PyObject *));
        if (op->ob_item == NULL) {
            Py_DECREF(op);
            return PyErr_NoMemory();
        }
    }
    Py_SET_SIZE(op, size);
    op->allocated = size;
    _PyObject_GC_TRACK(op);
    return (PyObject *) op;
}
  • struct _Py_list_state:列表缓冲池对象

      struct _Py_list_state {
          PyListObject *free_list[PyList_MAXFREELIST];
          int numfree;
      };
    • free_list:空闲列表的指针数组。
    • numfree:缓冲池存在的空闲list对象数量。
  • Python首先检查缓冲池中是否有空闲对象,如果没有,则通过PyObject_GC_New函数创建一个,PyObject_GC_New对对象的垃圾回收机制有作用。
  • 如果为列表元素分配的空间大小为0,则是空列表op->ob_item = NULL,否则分配大小为size, sizeof(PyObject *)的空间。

PyList_SetItem

为列表设置元素

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 (!valid_index(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;
}
  • 首先进行类型检查
  • 然后进行索引检查,检查是否越界。
  • ((PyListObject *)op) -> ob_item + i;:索引i的内存地址。
  • Py_XSETREF(*p, newitem);:设置元素,引用计数+1。

PyList_Insert

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);
}
  • 检查传入参数类型后调用ins1函数。

    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;
      }
    
      assert((size_t)n + 1 < PY_SSIZE_T_MAX);
      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;
    }
    
  • 判断传入的PyObject *v是否为NULL。
  • 断言n+1不会比PY_SSIZE_T_MAX大。PY_SSIZE_T_MAX = ((Py_ssize_t)(((size_t)-1)>>1))
  • list_resize:检查列表空间大小,任何对列表空间操作都会调用list_resize函数,该函数在需要的时候扩展内存空间或者缩小内存空间,确保内存的利用是最佳的。
  • 由于Python支持负数索引,所以需要在底层更改为正数。
  • 移动元素,可知Python insert操作的时间复杂度为O(n)。
  • 引用计数+1,插入元素。

PyList_Append

末尾添加元素

int
PyList_Append(PyObject *op, PyObject *newitem)
{
    if (PyList_Check(op) && (newitem != NULL))
        return app1((PyListObject *)op, newitem);
    PyErr_BadInternalCall();
    return -1;
}
static int
app1(PyListObject *self, PyObject *v)
{
    Py_ssize_t n = PyList_GET_SIZE(self);

    assert (v != NULL);
    assert((size_t)n + 1 < PY_SSIZE_T_MAX);
    if (list_resize(self, n+1) < 0)
        return -1;

    Py_INCREF(v);
    PyList_SET_ITEM(self, n, v);
    return 0;
}
  • list_resize对空间大小进行处理后,直接调用PyList_SET_ITEM,添加元素。

list_remove

删除元素

static PyObject *
list_remove(PyListObject *self, PyObject *value)
/*[clinic end generated code: output=f087e1951a5e30d1 input=2dc2ba5bb2fb1f82]*/
{
    Py_ssize_t i;

    for (i = 0; i < Py_SIZE(self); i++) {
        PyObject *obj = self->ob_item[i];
        Py_INCREF(obj);
        int cmp = PyObject_RichCompareBool(obj, value, Py_EQ);
        Py_DECREF(obj);
        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;
}
  • list_remove先使用for循环,对列表中的每一个元素进行比较。
  • 找到后调用list_ass_slice函数进行删除。list_ass_slice不是专门的删除函数,它有两个功能:

    • if v == null: del a[low:high]
    • if v != null: v[low:high] = v

list_dealloc

static void
list_dealloc(PyListObject *op)
{
    Py_ssize_t i;
    PyObject_GC_UnTrack(op);
    Py_TRASHCAN_BEGIN(op, list_dealloc)
    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);
    }
    struct _Py_list_state *state = get_list_state();
#ifdef Py_DEBUG
    // list_dealloc() must not be called after _PyList_Fini()
    assert(state->numfree != -1);
#endif
    if (state->numfree < PyList_MAXFREELIST && PyList_CheckExact(op)) {
        state->free_list[state->numfree++] = op;
    }
    else {
        Py_TYPE(op)->tp_free((PyObject *)op);
    }
    Py_TRASHCAN_END
}
  • PyObject_GC_UnTrack(op); Py_TRASHCAN_BEGIN(op, list_dealloc):Python的内存回收机制。
  • 在第一个if里减少列表元素的引用计数,释放ob_item内存空间。
  • 第二个if,判断列表缓冲池是否已满,没满则将该空列表放入缓冲池。已满则释放。

      >>> a = [1,2,4]
      >>> b = [1]
      >>> id_a=id(a)
      >>> id_b=id(b)
      >>> id_a
      2074874053808
      >>> id_b
      2074874243712
      >>> del a
      >>> c = []
      >>> id_c = id(c)
      >>> id_a == id_c
      True
      >>>
    

声明:Hello World|版权所有,违者必究|如未注明,均为原创|本网站采用BY-NC-SA协议进行授权

转载:转载请注明原文链接 - Python listobject 列表对象


我的朋友,理论是灰色的,而生活之树是常青的!