python 3.8.2 / 内置的数据结构 / list (类似于 STL 中的 vector)
一、特點
(1)相對于 tuple 來說,list 是動態的(mutable),即:各個元素都是可變的。
(2)可以通過索引進行查詢。
(3)list 中的元素可以是 python 中的任何對象。例如:list、tuple、dict、set、字符串和整數,并且可以任意混合。
(4)所有元素由一個中括號“[ ]”包裹。
二、相關操作
1、增
? ? ? ? a、append() ,在 list 尾部插入元素。
? ? ? ? b、insert(),在 list 的指定位置插入元素。
2、刪
? ? ? ? a、pop(),彈出 list 尾部元素并返回,與 append 對應。
? ? ? ? b、remove(),刪除指定元素。
? ? ? ? c、del ,刪除指定的位置的元素。
3、改
? ? ? ? 直接用“=”修改指定元素即可。
4、查
? ? ? ? 類似于數組的索引。
栗子:
if __name__ == '__main__':testlist = ['one', 'two', 'six']# 增# append 在 list 末尾添加元素。testlist.append('three')# insert 在指定的位置插入元素。testlist.insert(1, 'four')# 刪# pop 刪除 list 尾部元素。t = testlist.pop()# remove 刪除 list 中的元素。testlist.remove('two')# del 刪除 list 中指定范圍的元素。del testlist[0:2]# 改testlist[0] = 'hello world!'三、實現原理
1、底層的實現方式是 PyObject 類型的二維指針數組 。在 python 世界中,一切都是對象,無論是 int 、string 還是 list 等,這些都繼承于 PyObject ,所以 list 保存的是各個 PyObject 的指針,傳指針相對于傳對象效率更高。
2、空 list 的大小是 40B,即:一個描述 list 的結構體的大小。該結構體如下所示:
typedef struct {PyObject_VAR_HEAD // 用來保存已使用的內存槽的數量。PyObject **ob_item; // 用來保存對象的指針的指針數組。Py_ssize_t allocated; // 預先分配的內存槽的總容量,即:ob_item 數組的容量。 } PyListObject;3、當一個空的 list 執行 append 時,list 實際上會多分配一些內存,這樣可以提高下次 append 的高效性,即:時間復雜度為 O(1)。分配內存的算法如下:
new_allocated = (size_t)newsize + (newsize >> 3) + (newsize < 9 ? 3 : 6);?總體效果是隨著對象的數量增多,單次分配的內存槽逐漸變大。
4、append() 源碼實現
(1)過程簡述
- 判斷是否需要重新對 list 的內存槽數組(ob_item)進行重新分配。若需要,則申請新的內存槽數組、將舊內存槽數組中的數據拷貝到新的內存槽數組中,釋放舊的內存槽數組。
- 將新對象的指針加入到 ob_item 的尾部。
(2)append() 函數實際上調用的是 app1() 函數。
/*** 向 list 的尾部添加對象。 */ static PyObject *list_append(PyListObject *self, PyObject *object) {if (app1(self, object) == 0)Py_RETURN_NONE;return NULL; }(3)app1 函數的執行過程如下:
static int app1(PyListObject *self, PyObject *v) {/*** 返回當前 list 中對象的數量。*/Py_ssize_t n = PyList_GET_SIZE(self);assert(v != NULL);if (n == PY_SSIZE_T_MAX){PyErr_SetString(PyExc_OverflowError,"cannot add more objects to list");return -1;}/*** 重新調整 list 的內存。* 創建新的內存槽數組、釋放舊的內存槽數組。*/if (list_resize(self, n + 1) < 0)return -1;/*** 對象 v 的引用計數 + 1 。*/Py_INCREF(v);/*** 將對象指針插入到 list 的 ob_item 指針數據中。*/PyList_SET_ITEM(self, n, v);return 0; }(4)精髓就在 list_resize() 函數中了,該函數完成了內存的重新分配。
static int list_resize(PyListObject *self, Py_ssize_t newsize) {PyObject **items;size_t new_allocated, num_allocated_bytes;Py_ssize_t allocated = self->allocated;/* Bypass realloc() when a previous overallocation is large enoughto accommodate the newsize. If the newsize falls lower than halfthe allocated size, then proceed with the realloc() to shrink the list.*//*** (1)若 newsize < 已分配的內存槽的數量的 1/2,則重新分配內存槽數組(縮容)。* (2)若 newsize >= 已分配的內存槽的數量的 1/2,且 newsize =< 已分配的內存槽的數量,則無需調整。* (3)若 newsize > 已分配的內存槽的數量,則重新分配內存槽數組(擴容)。* (注意,新的 list 和舊的 list 不在同一個內存起始地址。)*/if (allocated >= newsize && newsize >= (allocated >> 1)){assert(self->ob_item != NULL || newsize == 0);Py_SIZE(self) = newsize;return 0;}/* This over-allocates proportional to the list size, making room* for additional growth. The over-allocation is mild, but is* enough to give linear-time amortized behavior over a long* sequence of appends() in the presence of a poorly-performing* system realloc().* The growth pattern is: 0, 4, 8, 16, 25, 35, 46, 58, 72, 88, ...* Note: new_allocated won't overflow because the largest possible value* is PY_SSIZE_T_MAX * (9 / 8) + 6 which always fits in a size_t.*//*** 內存分配方案,獲取新的內存槽的數量。*/new_allocated = (size_t)newsize + (newsize >> 3) + (newsize < 9 ? 3 : 6);if (new_allocated > (size_t)PY_SSIZE_T_MAX / sizeof(PyObject *)){PyErr_NoMemory();return -1;}if (newsize == 0)new_allocated = 0;/*** 新的內存槽數組的字節總數。*/num_allocated_bytes = new_allocated * sizeof(PyObject *);/*** (1)創建新的內存槽數組;* (2)將舊內存槽數組中的數據拷貝到新的內存槽數組中;* (3)釋放舊的內存槽數組。* obmalloc.c PyMem_realloc() 函數。*/items = (PyObject **)PyMem_Realloc(self->ob_item, num_allocated_bytes);if (items == NULL){PyErr_NoMemory();return -1;}/*** 將新的狀態更新到 list 對象中。*/self->ob_item = items;Py_SIZE(self) = newsize;self->allocated = new_allocated;return 0; }5、insert() 源碼實現
(1)過程簡述
- 判斷是否需要重新對 list 的內存槽數組(ob_item)進行重新分配。若需要,則申請新的內存槽數組、將舊內存槽數組中的數據拷貝到新的內存槽數組中,釋放舊的內存槽數組。
- 將 where 之后的元素全部向后挪一位。
- 將 new item 放到 where 的位置上。
(2)append() 函數實際上調用的是 ins1() 函數。
int PyList_Insert(PyObject *op, Py_ssize_t where, PyObject *newitem) {if (!PyList_Check(op)){PyErr_BadInternalCall();return -1;}return ins1((PyListObject *)op, where, newitem); }(3)ins1() 源碼
static int ins1(PyListObject *self, Py_ssize_t where, PyObject *v) {/*** 返回當前已用的內存槽的數量,即:list 中已有的元素的數量。*/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;/*** 將 where 之后的數據向后挪一位。*/for (i = n; --i >= where;)items[i + 1] = items[i];Py_INCREF(v);items[where] = v;return 0; }四、拓展
下面是兩種創建空 list 的方法,哪一個效率更高呢?
empty_list = [] empty_list = list()答案是前者效率更高,因為前者是內置的C函數,可以直接調用;后者是 python 的函數調用,會創建 stack 和參數檢查,比較浪費時間。
?
(SAW:Game Over!)
總結
以上是生活随笔為你收集整理的python 3.8.2 / 内置的数据结构 / list (类似于 STL 中的 vector)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 命令 / Linux / 常用的解压缩的
- 下一篇: python自动发送微信-Python自