Python 列表原始碼解析
列表作為python 最常用的一種資料型別, 一直很好奇其是如何實現動態的擴充套件的,於是上github看了原始碼(https://github.com/python/cpython/blob/master/Include/listobject.h, https://github.com/python/cpython/blob/master/Objects/listobject.c),結合《python原始碼剖析》分享一下自己的收穫!
列表原始碼定義
#ifndef Py_LIMITED_API
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;
#endif
allocated 記錄了這個列表申請的記憶體大小, 假如一個列表中可以容納10個元素,則allocated 就是10,當我們往列表中新增或者刪除元素時這個大小都會跟著改變
ob_size 則是記錄了當前列表中 已經使用的大小
allocated 和 ob_size 都和記憶體管理有關,python的列表總是會被頻繁的新增或者刪除元素,因此頻繁的申請釋放記憶體顯然是不明智的,所以python的列表在建立時總是會申請一大塊記憶體,申請的記憶體大小就記錄在 allocated 上, 已經使用的就記錄在 ob_size
列表建立
通過PyAPI_FUNC(PyObject *) PyList_New(Py_ssize_t size);
方法來建立
該方法需要指定 建立列表容量的大小
建立列表程式碼如下
PyList_New(Py_ssize_t size)
{
PyListObject *op;
#ifdef SHOW_ALLOC_COUNT
static int initialized = 0;
if (!initialized) {
Py_AtExit(show_alloc);
initialized = 1;
}
#endif
if (size < 0) {
PyErr_BadInternalCall();
return NULL;
}
if (numfree) {
numfree--;
op = free_list[numfree];
_Py_NewReference((PyObject *)op);
#ifdef SHOW_ALLOC_COUNT
count_reuse++;
#endif
} else {
op = PyObject_GC_New(PyListObject, &PyList_Type);
if (op == NULL)
return NULL;
#ifdef SHOW_ALLOC_COUNT
count_alloc++;
#endif
}
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();
}
}
Py_SIZE(op) = size;
op->allocated = size;
_PyObject_GC_TRACK(op);
return (PyObject *) op;
}
新建列表主要做了:
1 引數檢查 列表大小不能是 0
2 檢查是否有快取可用
3 記憶體檢查 檢查新建的列表大小不能超過 記憶體大小
4 申請記憶體 新建列表
5 為allocated 設定初始化的值
列表設定元素的實現
lst = list()
lst[0] = "hello world"
當通過 PyObject_GC_New 建立列表之後,其實裡面的元素都是null
假如 我們要給lst 第n元素賦值 其實就是通過 PyList_SetItem(PyObject *op, Py_ssize_t i,
方法來實現的
PyObject *newitem)
程式碼如下:
PyList_SetItem(PyObject *op, Py_ssize_t i,
PyObject *newitem)
{
PyObject **p;
if (!PyList_Check(op)) {
Py_XDECREF(newitem);
PyErr_BadInternalCall();
return -1;
}
if (i < 0 || 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 設定元素
列表 insert 插入元素的實現
插入元素 使用的是ins1(PyListObject *self, Py_ssize_t where, PyObject *v)
方法來實現
原始碼 如下
ins1(PyListObject *self, Py_ssize_t where, PyObject *v)
{
Py_ssize_t i, n = Py_SIZE(self);
PyObject **items;
if (v == NULL) {
PyErr_BadInternalCall();
return -1;
}
if (n == PY_SSIZE_T_MAX) {
PyErr_SetString(PyExc_OverflowError,
"cannot add more objects to list");
return -1;
}
if (list_resize(self, n+1) < 0)
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; )
items[i+1] = items[i];
Py_INCREF(v);
items[where] = v;
return 0;
}
其主要實現過程 如下
1 引數檢查
2 從新調整列表容量 通過 list_resize 方法確定 是否需要申請記憶體
3 確定插入點
4 插入元素
可以看到列表插入時 都會將後面的位置的元素重新移動
列表的append 實現
通過 PyList_Append(PyObject *op, PyObject *newitem)
方法來實現
原始碼如下:
PyList_Append(PyObject *op, PyObject *newitem)
{
if (PyList_Check(op) && (newitem != NULL))
return app1((PyListObject *)op, newitem);
PyErr_BadInternalCall();
return -1;
}
其主要是呼叫了 app1(PyListObject *self, PyObject *v)
來實現的
app1 方法主要做了以下工作
1 引數檢查
2 容量檢查
3 呼叫
list_resize
方法檢查是否需要申請記憶體4 新增元素
列表轉元組
列表轉元組其實就是新建一個大小和列表一樣大小的陣列,並將該列表內的元素新增到元組中
原始碼如下:
PyObject *
PyList_AsTuple(PyObject *v)
{
PyObject *w;
PyObject **p, **q;
Py_ssize_t n;
if (v == NULL || !PyList_Check(v)) {
PyErr_BadInternalCall();
return NULL;
}
n = Py_SIZE(v);
w = PyTuple_New(n);
if (w == NULL)
return NULL;
p = ((PyTupleObject *)w)->ob_item;
q = ((PyListObject *)v)->ob_item;
while (--n >= 0) {
Py_INCREF(*q);
*p = *q;
p++;
q++;
}
return w;
}
列表的反轉 reverse
列表的反轉是就地反轉的,不會生成新的列表, 迴圈當前列表 使用了一個第三變數,然後將元素移位
static void
reverse_slice(PyObject **lo, PyObject **hi)
{
assert(lo && hi);
--hi;
while (lo < hi) {
PyObject *t = *lo;
*lo = *hi;
*hi = t;
++lo;
--hi;
}
}
關於 列表的 extend 方法 看了原始碼才知道,只要是可迭代物件都可以放入extend(), 字典,元組,字串都可以
部分 原始碼定義 如下
if (PyList_CheckExact(iterable) || PyTuple_CheckExact(iterable) ||
(PyObject *)self == iterable) {
PyObject **src, **dest;
iterable = PySequence_Fast(iterable, "argument must be iterable");
if (!iterable)
return NULL;
還有很多的方法等著你們去探索和發現, 更多列表實現的原始碼請看:
https://github.com/python/cpython/blob/master/Objects/listobject.c