内部结构#

list 对象是一种 变长对象 ,因此包含变长对象公共头部。 除了公共头部, list 内部维护了一个动态数组,而 数组则依次保存元素对象的指针:
- **ob_item ,指向动态数组的指针,动态数组保存元素对象的指针;
- allocated ,动态数组 长度,即列表 容量;
- ob_size ,动态数组 当前保存元素个数,即列表 长度 。

对象操作#
append#
python中的list的实现非常像 C++ 中的Vector
1
l = list() # 初始化一个空列表

如果我们append('a') 1个元素:

ob_size 变为 1, ob_item 指向一个 大小为4的新内存块
相继append三个元素:

ob_size 变为 4, 内存块满了
大多数情况下, append 方法性能都足够好,时间复杂度是 O(1)。
动态扩容#
继续append一个元素:
这是resize相关代码
1
2
3
4/* cpython/Objects/listobject.c */
/* The growth pattern is: 0, 4, 8, 16, 25, 35, 46, 58, 72, 88, ... */
/* currently: new_allocated = 5 + (5 >> 3) + 3 = 8 */
new_allocated = (size_t)newsize + (newsize >> 3) + (newsize < 9 ? 3 : 6); # 扩容时,预留一定的余量,(newsize >> 3)表明余量一般是 1/8 左右
如果 list 对象内部数组已用满,再添加元素时则需要进行扩容。 append 等方法在操作时都会对内部数组进行检查,如需扩容则调用 list_resize 函数。在 list_resize 函数, Python 重新分配一个长度更大的数组并替换旧数组。
由于内部数组扩容时,需要将列表元素 从旧数组拷贝到新数组,时间复杂度为 O(n) ,开销较大。为此, Python 在为内部数组扩容时,会预留一定余量,一般是 1/8 左右 。
pop#
1 | >>> l.pop() |

我们可以看到这里只是简单将 元素e 弹出,内存布局并未发生变化。
动态减容#
实际上,每次pop,都会调用 resize 函数。1 | /* cpython/Objects/listobject.c */ |
1 | >>> l.pop() |
