《Python源码剖析》之 list对象

定义

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 = newsize
else
    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,则直接释放对象内存。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值