《数据结构与算法》是计算机一门必修课,不管是在哪个大学。记得当时学这门课程是还是有C语言实现的,接触到的第一种数据结构是线性表中的顺序表,是使用数组实现,结构代码如下:
#define MAXSIZE 20
typedef int ElemType;
typedef struct
{
ElemType data[MAXSIZE];
int length ;//顺序表当前长度
}Sqlist;
这里封装了一个结构,其实是对数据进行封装,数据的最大长度定义为MAXSIZE,另外增加一个记录数据当前长度的变量。
顺序表最常用的两个操作是插入和删除,实现的代码网上都有。不过对于这种静态的顺序表有个问题,就是一旦达到最大的长度就不能再进行数据插入,所以还有一种动态的顺序表结构;
typedef int DataType;
typedef struct SeqList
{
DataType* _a;
size_t _size; // 有效数据个数
size_t _capacity; // 容量
}SeqList;
这里就用到了C语言的精髓——指针,简单来说就是你可以动态扩容。具体实现参考 https://blog.csdn.net/qq_34772530/article/details/78804329
用过Pyhton的道友们,都对“人生苦短,我用 Python”这句话深有体会。
Python中也有对应的顺序表结构——List,对应的方法都已经封装好,虽然其底层还是有C实现,但是直接使用就是舒服。
不过有时也会很好奇其如何定义内部结构?如何实现动态地插入扩展?
So,上GitHub找了源码,再结合《python源码剖析》还有其他博主分享,记录下理解到的内容。
cpython-master\Include\cpython\listobject.h
typedef struct {
PyObject_VAR_HEAD
/* Vector of pointers to list elements. list[0] is ob_item[0], etc. */
PyObject **ob_item;
/* ob_item contains space for 'allocated' elements. The number
* currently in use is ob_size.
* Invariants:
* 0 <= ob_size <= allocated
* len(list) == ob_size
* ob_item == NULL implies ob_size == allocated == 0
* list.sort() temporarily sets allocated to -1 to detect mutations.
*
* Items must normally not be NULL, except during construction when
* the list is not yet visible outside the function that builds it.
*/
Py_ssize_t allocated;
} PyListObject;
在cpython-master\Include\object.h中有对PyObject_VAR_HEAD的定义
/* PyObject_VAR_HEAD defines the initial segment of all variable-size
* container objects. These end with a declaration of an array with 1
* element, but enough space is malloc'ed so that the array actually
* has room for ob_size elements. Note that ob_size is an element count,
* not necessarily a byte count.
*/
PyObject_VAR_HEAD 中的ob_size记录着当前列表中的元素个数;
PyObject **ob_item 则是指向列表所在内存块首地址的指针;
allocated 记录了这个列表申请的内存大小;
ob_size 和 allocated 关系如下
0 <= ob_size <= allocated
len(list) == ob_size
ob_item == NULL 则意味着 ob_size == allocated == 0
cpython-master\Include\cpython\listobject.h
PyObject *
PyList_New(Py_ssize_t size)
{
//1, 参数检查 列表大小不能是 0
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
//2,为PyListObject对象申请空间
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;
}
}
//3,为PyListObject对象中的元素申请空间
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();
}
}
//4,维护allocated和ob_size
Py_SET_SIZE(op, size);
op->allocated = size;
_PyObject_GC_TRACK(op);
return (PyObject *) op;
}
列表创建主要有以下过程:
1,参数检查 列表大小不能是 0
2,创建新的PyListObject对象,这里使用到对象级缓冲池技术(不懂),
检查free_list中是否有可用对象,如果有,直接使用可用对象;
如果缓冲池所有对象都不可以使用,则通过PyObject_GC_New在系统堆申请内存,创建新对象。
3,创建新的PyListObject对象后,立即根据size参数创建PyListObject对象维护的列表元素。
4,完成PyListObject对象和列表的创建后,维护allocated和ob_size
cpython-master\Include\cpython\listobject.h
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;
}
赋值操作主要有以下过程
1,参数类型检查
2,索引有效性检查,不可超出索引
3,元素赋值
cpython-master\Include\cpython\listobject.h
插入元素 使用的是ins1(PyListObject *self, Py_ssize_t where, PyObject *v) 方法来实现
static int
ins1(PyListObject *self, Py_ssize_t where, PyObject *v)
{
Py_ssize_t i, n = Py_SIZE(self);
PyObject **items;
//1,参数检查
if (v == NULL) {
PyErr_BadInternalCall();
return -1;
}
//2,列表容量调整
assert((size_t)n + 1 < PY_SSIZE_T_MAX);
if (list_resize(self, n+1) < 0)
return -1;
//3,确定插入点
if (where < 0) {
where += n;
if (where < 0)
where = 0;
}
if (where > n)
where = n;
//4,插入元素
items = self->ob_item;
for (i = n; --i >= where; )
items[i+1] = items[i];
Py_INCREF(v);
items[where] = v;
return 0;
}
插入元素主要有以下过程
1,参数检查
2,列表容量调整,使用list_resize函数维护容量,其中有算法来判断是否需要申请内存扩展列表,具体实现可以研究下这个函数
3,确定插入点,由于List有负数索引,所以必须处理负数的情形。
4,确定插入位置后,开始挪动元素,将插入点之后的元素向后移动一位,这样插入点就空出一个位置。
cpython-master\Include\cpython\listobject.h
删除元素使用的是list_remove(PyListObject *self, PyObject *value) 方法来实现
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;
}
删除元素主要有以下过程
1,对整个列表进行遍历,将待删除的元素与PyListObject中的每个元素进行比较,如果匹配到则调用
list_ass_slice函数。
2,list_ass_slice根据v是否为NULL,判断是replace还是delete;
例如
l = [1,2,3,4]
ilow = 1,ihigh=3,v=[‘a’,‘b’]
l = [1,‘a’,‘b’,‘4’]
ilow = 0,ihigh=2,v=[]
l = [‘b’,‘4’]
list_ass_slice的实现看了好复杂,也没看明白,感兴趣的可以研究一波,不过感觉C要很熟悉才看的明白。
cpython-master\Include\cpython\listobject.h中还有Lits其他方法的具体实现,有兴趣可以研究一波。Pyhton虽然很强大,但是感觉C语言才是真正的强大。
参考:
《python源码剖析》
https://blog.csdn.net/lucky404/article/details/79596319