《Python源码剖析》之 list对象

来源:转载


定义


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

说明


PyObject_VAR_HEAD
PyListObject是变长对象
PyObject **ob_item;
指向列表元素的指针数组, list[0] 即 ob_item[0]
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;
}

简化步骤


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

结论


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里数组的概念。
插入


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

示例


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

append


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

示例


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

extend


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

示例


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

删除


找到要删除元素位置
删除之, 后面元素前移
删除元素操作:实质是函数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,则直接释放对象内存。




分享给朋友:
您可能感兴趣的文章:
随机阅读: