前言
本文所讲Python
实现均为CPython
,需读者具备一定的C
语言阅读能力。本博文参考了**《Python源码剖析》**与Python2.7
源码。PyListObject
采用顺序存储(而非链式存储),熟悉数据结构
的读者,能很容易明白本博文所讲内容。
介绍
PyListObject
是Python
提供的List
容器实现,与C++ STL
中的vector
实现机制相近。PyListObject
是变长对象同时也是可变对象(很显然,不同时刻List
中可以存在不同数目的元素)。
PyListObject
定义如下:
1 2 3 4 5
| typedef struct { PyObject_VAR_HEAD PyObject **ob_item; int allocated; } PyListObject;
|
PyObject_VAR_HEAD
中的ob_size
与PyListObject
中的allocated
字段分别标识了容器的现有*元素个数(size)**与容器容量(capacity)***。ob_item
为指向PyObject *
的指针(即PyObject *
数组),是PyListObject
实现顺序存储的数组。
实现
1、创建对象
Python
提供了唯一创建List
的函数——PyList_New
。下面是简化的后Python
创建PyListObject
对象的过程。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
| #define MAXFREELISTS 80 static PyListObject *free_lists[MAXFREELISTS]; static int num_free_ists = 0;
PyObject *PyList_New(int size) { PyListObject *op; size_t nbytes; nbytes = size * sizeof(PyObject *); if(nbytes / sizeof(PyObject *) != (size_t)size) { return PyErr_NoMemory(); } if(num_free_lists) { num_free_lists--; op = free_lists[num_free_lists]; _Py_NewReference((PyObject *)op); } else { op = PyObject_GC_New(PyListObject, &PyList_Type); } if(size <= 0) { op->ob_item = NULL; } else { op->ob_item = (PyObject **)PyMem_MALLOC(nbytes); memset(op->ob_item, 0, nbytes); } op->ob_size = size; op->allocated = size; return (PyObject *)op; }
|
PyListObject
对象分为两部分:①PyListObject
对象②PyListObject
对象容纳的PyObject
元素。
2、设置元素
前面提到PyListObject
是顺序存储,可以**随机访问**。通过下标设置List
中元素值,是由PyList_SetItem
函数实现的。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| int PyList_SetItem(register PyObject *op, register int i, register PyObject *new_item) { register PyObject *olditem; register PyObject **p; if(i < 0 || i>= (PyListObject)op->ob_size) { return -1; } p = ((PyListObject*)op)->ob_item + i; olditem = *p; Py_XDECREF(olditem); return 0; }
|
3、插入元素
了解顺序存储
的读者,很容易想到新元素的插入会导致元素的移动。PyListObject
的实现也不例外,而这其中又牵扯了PyListObject.ob_item
的*扩容**与缩容***(参考Redis
或者其它若干软件的实现,都会有类似机制)。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| int PyList_Insert(PyObject *op, Py_ssize_t where, PyObject *new_item) { return insl((PyListObject *)op, where, newitem); }
static int insl(PyListObject *self, Py_ssize_t where, PyObject *v) { Py_ssize_t i, n = self->ob_size; PyObject **items; if(list_resize(self, n+1) == -1) 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; ) item[i+1] = item[i] Py_INCREF(v); item[where] = v; return 0; }
|
其中函数list_resize
为PyListObject
对象*扩容**与缩容***的关键。list_resize
函数的实现如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| static int list_resize(PyObjectList *self, int newsize) { PyObject **items; size_t new_allocated; int allocated = self->allocated; if(allocated >= newsize && newsize >= (allocated >> 1)) { self->ob_size = newsize; return 0; } new_allocated = (newsize >> 3) + (newsize < 9 ? 3 : 6) + newsize; if(newsize == 0) new_allocated = 0; items = self->ob_items; PyMem_RESIZE(item, PyObject *, new_allocated); self->ob_itme = items; self->ob_size = newsize; self->allocated = new_allocated; return 0; }
|
当List
新的元素个数newsize
,满足条件:allocated/2 <= newsize <= allocated
时,不需要进行realloc
。当newsize >= allocated
时,PyObjectList
会进行*扩容**操作,当newsize < allocated/2
时PyObjectList
会进行缩容***操作。
对象池
CPython
为了解决频繁创建对象带来的性能问题(大多数对性能要求较高的C
程序均采用类似机制),采用了大量的对象池
技术——PyListObject
的实现也不例外。如果读者对此类技术不熟悉,请参阅**对象池**设计模式。
在如上PyList_New
函数的实现代码中,free_lists
指针数组便是用于PyListObject
创建的对象池。我们可以看到如果存在可用的PyListObject
,Python
便会从对象池
中取出并返回一个PyListObject
对象。那么PyListObject
对象是**何时、如何**归还给对象池的呢?答案就在销毁PyListObject
的list_dealloc
函数里。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| static void list_dealloc(PyListObject *op) { int i; if(op->ob_item != NULL) { i = op->ob_size; while(--i >= 0) { Py_XDECREF(op->ob_item[i]); } PyMem_FREE(op->ob_item); } if(num_free_lists < MAXFREELISTS && PyList_CheckExact(op)) free_lists[num_free_lists++] = op; else op->ob_type->tp_free((PyObject *)op); }
|