《Python源码剖析》之 list对象

来源:互联网 发布:windows搭建ftp 编辑:程序博客网 时间:2024/06/16 15:16

定义

typedef struct {    PyObject_VAR_HEAD  //list对象是变长对象,所以有变长对象头    PyObject **ob_item; //真正的存储容器,用来存储PyObject对象指针。    Py_ssize_t allocated; //allocated表示list已分配了多少存储空间。} PyListObject;

说明

  1. PyObject_VAR_HEAD
    PyListObject是变长对象
  2. PyObject **ob_item;
    指向列表元素的指针数组, list[0] 即 ob_item[0]
  3. Py_ssize_t allocated;
    allocated列表分配的空间, ob_size为已使用的空间
    allocated 总的申请到的内存数量
    ob_size 实际使用内存数量
    等式:
    0 <= ob_size <= allocated
    len(list) == ob_size
    ob_item == NULL implies ob_size == allocated == 0
    这里写图片描述

构造方法

PyObject *PyList_New(Py_ssize_t size){    PyListObject *op;    size_t nbytes;#ifdef SHOW_ALLOC_COUNT    static int initialized = 0;    if (!initialized) {        Py_AtExit(show_alloc);        initialized = 1;    }#endif    // 大小为负数, return    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();    // 计算需要的字节数(PyObject指针数组)    nbytes = size * sizeof(PyObject *);    // 如果缓冲池非空, 从缓冲池取    if (numfree) {        // 取缓冲池数组最后一个对象        numfree--;        op = free_list[numfree];        // set refcnt=1        _Py_NewReference((PyObject *)op);#ifdef SHOW_ALLOC_COUNT        count_reuse++;#endif    } else {        // 否则, GC_New分配内存空间        op = PyObject_GC_New(PyListObject, &PyList_Type);        // 分配失败        if (op == NULL)            return NULL;#ifdef SHOW_ALLOC_COUNT        count_alloc++;#endif    }    // 确定ob_item列表元素指针的值    // 若大小<=0    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);    }    // ob_size = size    Py_SIZE(op) = size;    // allocated    op->allocated = size;    // gc用    _PyObject_GC_TRACK(op);    return (PyObject *) op;}

简化步骤

  1. 判断列表缓冲池是否为空, 是的话从缓冲池取(复用)
  2. 否则, 从内存中分配空间
  3. 然后初始化数据

结论

Py_SIZE(op) = size;
op->allocated = size;
第一次生成list, 其allocated = ob_size

list_resize函数:

int list_resize(PyListObject *self, Py_ssize_t newsize)

用法:

extends方法, list_resize(self, m + n)
pop方法, list_resize(self, Py_SIZE(self) - 1)
append方法, list_resize(self, n+1)

定义:

/*如果allocated / 2 <= newsize <= allocated,则直接把ob_size设置成newsize。如果不在这个范围内,就按如下方案realloc内存:new_allocated = (newsize >> 3) + (newsize < 9 ? 3 : 6);*/if allocated/2 <= newsize <= allocated    allocated 不变    ob_size = newsizeelse    allocated =  newsize +   ((newsize >> 3) + (newsize < 9 ? 3 : 6))    ob_size = newsize

List的操作过程

插入元素操作:实质是函数ins1的包装。ins1函数的关键操作是,先通过list_resize(下面细说)调整list长度,然后确定插入点。由于python list的索引可以为负数(即末尾元素索引为-1),所以索引值小于0时得加上长度得到C数组的索引。接着将插入点后的元素向后搬运,在插入点写入对象。从此可以看出list就是C里数组的概念。
插入

  1. resize n+1
  2. 确定插入点
  3. 插入点后所有元素后移
  4. 执行插入

示例

>>> a = [1, 2, 3]>>> a.insert(0, 9)>>> a[9, 1, 2, 3]

append

  1. resize n+1
  2. 放入最后一个位置(ob_size)

示例

>>> a = [1, 2, 3]>>> a.append(4)>>> a[1, 2, 3, 4]

extend

  1. 计算两个list大小 m n
  2. resize m+n(此时本身被复制)
  3. 遍历长度为n的数组, 从ob_item+m的位置开始加入

示例

>>> m = [1, 2, 3]>>> n = [4, 5]>>> m.extend(n)>>> m[1, 2, 3, 4, 5]

删除

  1. 找到要删除元素位置
  2. 删除之, 后面元素前移
    删除元素操作:实质是函数app1的包装。app1函数的关键操作是,先找到第一个对象的位置,然后通过list_ass_slice函数将删除点前后的两段合并。

示例

>>> a = [1, 2, 3, 2]>>> a.remove(2)>>> a[1, 3, 2]

概念和现实

list的创建分两步。1. 创建list对象本身。2. 为ob_item分配内存。
list的销毁也分两步。1. 回收ob_item的内存。2. 销毁list对象本身。
这样的对象创建和销毁方案是为对象池(free_lists)服务的。

创建list阶段
Python会查看free_lists中是否有缓存对象。若有,则直接从free_lists取出。若没有,则从堆上分配list对象内存。实际上并没有实现

a=[1,2,3]b=[1,2,3]a is b  #输出false !!!

销毁list阶段
若缓存list数(num_free_lists)小于最大可缓存数(MAXFREELISTS ),则将list对象缓存到free_lists备用。若超过了num_free_lists,则直接释放对象内存。

原创粉丝点击