# Python List 实现原理
Python list 对象的一些介绍:https://www.runoob.com/python3/python3-list.html
基于 CPython3.9.12 版本阅读
在 CPython 中 list 对应 PyListObject,位于 objects/listobject.c
PyTypeObject PyList_Type = {  | |
PyVarObject_HEAD_INIT(&PyType_Type, 0)  | |
"list",  | |
sizeof(PyListObject),  | |
0,  | |
(destructor)list_dealloc, /* tp_dealloc */  | |
0, /* tp_vectorcall_offset */  | |
0, /* tp_getattr */  | |
0, /* tp_setattr */  | |
0, /* tp_as_async */  | |
(reprfunc)list_repr, /* tp_repr */  | |
0, /* tp_as_number */  | |
&list_as_sequence, /* tp_as_sequence */  | |
&list_as_mapping, /* tp_as_mapping */  | |
PyObject_HashNotImplemented, /* tp_hash */  | |
0, /* tp_call */  | |
0, /* tp_str */  | |
PyObject_GenericGetAttr, /* tp_getattro */  | |
0, /* tp_setattro */  | |
0, /* tp_as_buffer */  | |
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC |  | |
Py_TPFLAGS_BASETYPE | Py_TPFLAGS_LIST_SUBCLASS, /* tp_flags */  | |
list___init____doc__, /* tp_doc */  | |
(traverseproc)list_traverse, /* tp_traverse */  | |
(inquiry)_list_clear, /* tp_clear */  | |
list_richcompare, /* tp_richcompare */  | |
0, /* tp_weaklistoffset */  | |
list_iter, /* tp_iter */  | |
0, /* tp_iternext */  | |
list_methods, /* tp_methods */  | |
0, /* tp_members */  | |
0, /* tp_getset */  | |
0, /* tp_base */  | |
0, /* tp_dict */  | |
0, /* tp_descr_get */  | |
0, /* tp_descr_set */  | |
0, /* tp_dictoffset */  | |
(initproc)list___init__, /* tp_init */  | |
PyType_GenericAlloc, /* tp_alloc */  | |
PyType_GenericNew, /* tp_new */  | |
PyObject_GC_Del, /* tp_free */  | |
.tp_vectorcall = list_vectorcall,  | |
};  | 
分别定义了__new__方法和__init__,先来看看在调用 list () 时,做了什么,__new__调用了 PyType_GenericNew
PyObject * | |
PyType_GenericNew(PyTypeObject *type, PyObject *args, PyObject *kwds)  | |
{ | |
return type->tp_alloc(type, 0); // 先为对象分配空间  | |
} | 
__init__调用了 list___init__方法
static int  | |
list___init__(PyObject *self, PyObject *args, PyObject *kwargs)  | |
{ | |
int return_value = -1; // 返回值,-1 在 CPython 里表示异常,(hash 不会出现 - 1, 为 - 1 的哈希会返回 - 2)  | |
PyObject *iterable = NULL;  | |
if (Py_IS_TYPE(self, &PyList_Type) &&  | |
!_PyArg_NoKeywords("list", kwargs)) {  | |
	// 如果类型为 List 并且没有关键字,则直接结束 | |
goto exit;  | |
    } | |
if (!_PyArg_CheckPositional("list", PyTuple_GET_SIZE(args), 0, 1)) {  | |
	// 如果传递的位置参数不为 0 到 1 个, | |
goto exit;  | |
    } | |
if (PyTuple_GET_SIZE(args) < 1) {  | |
	// 如果位置参数参数小于 1,则跳到 skip_optional 创建 list, 但是是空列表 | |
goto skip_optional;  | |
    } | |
iterable = PyTuple_GET_ITEM(args, 0); // 获取 args 的第一个元素,也就是 [...]  | |
skip_optional: | |
return_value = list___init___impl((PyListObject *)self, iterable); // 创建列表  | |
exit: | |
return return_value;  | |
} | |
static int  | |
list___init___impl(PyListObject *self, PyObject *iterable)  | |
/*[clinic end generated code: output=0f3c21379d01de48 input=b3f3fe7206af8f6b]*/ | |
{ | |
    /* Verify list invariants established by PyType_GenericAlloc() */ | |
assert(0 <= Py_SIZE(self));  | |
assert(Py_SIZE(self) <= self->allocated || self->allocated == -1);  | |
assert(self->ob_item != NULL ||  | |
self->allocated == 0 || self->allocated == -1);  | |
    /* Empty previous contents */ | |
if (self->ob_item != NULL) {  | |
(void)_list_clear(self);  | |
    } | |
if (iterable != NULL) { // 如果传递了 [...] 对象,则进入 if statement  | |
if (_PyObject_HasLen(iterable)) {  | |
	    /* | |
_PyObject_HasLen 主要判断 iterable 对象是否实现了序列协议的对象相关字段附加结构 (tp_as_sequence) 或者  | |
是否实现了映射协议的对象相关字段的附加结构 (tp_as_mapping)  | |
*/  | |
Py_ssize_t iter_len = PyObject_Size(iterable);  | |
	    /* | |
PyObject_Size 主要用来获取 iterable 对象的长度,如果该对象定义了 tp_as_sequence 协议,  | |
则会判断 tp_as_sequence 协议是否实现__len__方法,定义了则调用__len__, 如果没有定义,  | |
则再次获取 tp_as_mapping 协议,并在 tp_as_mapping 协议里判断是否定义__len__,定义了则调用,如果两个协议都没有  | |
定义__len__, 则返回 - 1  | |
*/  | |
if (iter_len == -1) {  | |
if (!PyErr_ExceptionMatches(PyExc_TypeError)) {  | |
		    // 创建失败 | |
return -1;  | |
                } | |
PyErr_Clear();  | |
            } | |
if (iter_len > 0 && self->ob_item == NULL  | |
&& list_preallocate_exact(self, iter_len)) {  | |
		// 长度大于 0、ob_item 为 NULL, 用于真正存储列表的元素,list_preallocate_exact 为列表预分配内存,调用 C 的 malloc | |
return -1;  | |
            } | |
        } | |
PyObject *rv = list_extend(self, iterable); // 最主要的方法,代码太长,分开粘  | |
if (rv == NULL)  | |
return -1;  | |
Py_DECREF(rv);  | |
    } | |
return 0;  | |
} | 
经过源码分析发现,在调用 list ([…]) 时,实际上是调用了 list 对象的 extend 方法,代码也比较长,一点一点分析😵
static PyObject *  | |
list_extend(PyListObject *self, PyObject *iterable)  | |
/*[clinic end generated code: output=630fb3bca0c8e789 input=9ec5ba3a81be3a4d]*/ | |
{ | |
PyObject *it; /* iter(v) */  | |
Py_ssize_t m; /* size of self */  | |
Py_ssize_t n; /* guess for size of iterable */  | |
Py_ssize_t mn; /* m + n */  | |
    Py_ssize_t i; | |
PyObject *(*iternext)(PyObject *);  | |
    /* Special cases: | |
1) lists and tuples which can use PySequence_Fast ops  | |
2) extending self to self requires making a copy first  | |
*/  | |
if (PyList_CheckExact(iterable) || PyTuple_CheckExact(iterable) ||  | |
(PyObject *)self == iterable) { // 判断 iterable 是否为 list 类型或者 tuple 类型  | |
PyObject **src, **dest; // 两个二级指针  | |
iterable = PySequence_Fast(iterable, "argument must be iterable");  | |
if (!iterable)  | |
return NULL;  | |
n = PySequence_Fast_GET_SIZE(iterable); // 获取 iterable 的大小  | |
if (n == 0) {  | |
            /* short circuit when iterable is empty */ | |
Py_DECREF(iterable);  | |
            Py_RETURN_NONE; | |
        } | |
m = Py_SIZE(self); // 获取自身的大小  | |
        /* It should not be possible to allocate a list large enough to cause | |
an overflow on any relevant platform */  | |
assert(m < PY_SSIZE_T_MAX - n);  | |
if (list_resize(self, m + n) < 0) { // 扩容操作,比较重要,后面会讲  | |
Py_DECREF(iterable);  | |
return NULL;  | |
        } | |
        /* note that we may still have self == iterable here for the | |
* situation a.extend(a), but the following code works  | |
* in that case too. Just make sure to resize self  | |
* before calling PySequence_Fast_ITEMS.  | |
*/  | |
        /* populate the end of self with iterable's items */ | |
src = PySequence_Fast_ITEMS(iterable); // 获取 iterable 的 ob_item, 真正用于存储元素  | |
dest = self->ob_item + m;  | |
for (i = 0; i < n; i++) { // 循环操作,将 iterable 里的每个元素设置到 self->ob_item 里  | |
PyObject *o = src[i];  | |
Py_INCREF(o);  | |
dest[i] = o;  | |
        } | |
Py_DECREF(iterable); // 引用计数 - 1  | |
Py_RETURN_NONE; // 返回 None, 结束  | |
    } | |
    // 如果 iterable 不是 list 或者 tuple 类型,则会走到这 | |
it = PyObject_GetIter(iterable); // 获取 iterable 的__iter__方法、调用,获取 iterator  | |
if (it == NULL)  | |
	// 如果__iter__返回空则调用结束,失败 | |
return NULL;  | |
iternext = *Py_TYPE(it)->tp_iternext; // 从 iterator 中获取__next__方法  | |
    /* Guess a result list size. */ | |
n = PyObject_LengthHint(iterable, 8); // 猜测结果列表的大小  | |
if (n < 0) {  | |
	// 如果小于 0, 失败,结束调用 | |
Py_DECREF(it);  | |
return NULL;  | |
    } | |
m = Py_SIZE(self); // 获取自身的大小  | |
if (m > PY_SSIZE_T_MAX - n) { // 表示溢出  | |
        /* m + n overflowed; on the chance that n lied, and there really | |
* is enough room, ignore it. If n was telling the truth, we'll  | |
* eventually run out of memory during the loop.  | |
*/  | |
    } | |
else {  | |
mn = m + n; // 自身大小加上 iterator 的大小  | |
        /* Make room. */ | |
if (list_resize(self, mn) < 0) // 判断是否需要扩容  | |
goto error;  | |
        /* Make the list sane again. */ | |
Py_SET_SIZE(self, m); // 继续保持大小  | |
    } | |
    /* Run iterator to exhaustion. */ | |
for (;;) {  | |
	// 死循环,不断调用__next__, 直到消耗完 | |
PyObject *item = iternext(it);  | |
if (item == NULL) {  | |
	    //__next__返回空,结束循环 | |
if (PyErr_Occurred()) {  | |
if (PyErr_ExceptionMatches(PyExc_StopIteration))  | |
PyErr_Clear();  | |
                else | |
goto error;  | |
            } | |
break;  | |
        } | |
if (Py_SIZE(self) < self->allocated) { // 如果自身大小小于已分配大小,将元素设置到最后的位置,并重新设置大小  | |
            /* steals ref */ | |
PyList_SET_ITEM(self, Py_SIZE(self), item);  | |
Py_SET_SIZE(self, Py_SIZE(self) + 1); // 自身大小 ob_size + 1  | |
        } | |
else {  | |
	    // 没有大于已分配大小 | |
int status = app1(self, item); // 调用 app1 设置元素  | |
Py_DECREF(item); /* append creates a new ref */  | |
if (status < 0)  | |
		// 小于 0 可能因为内存溢出或者扩容失败 | |
goto error;  | |
        } | |
    } | |
    /* Cut back result list if initial guess was too large. */ | |
if (Py_SIZE(self) < self->allocated) {  | |
	// 这里也比较重要,再次判断自身大小是否小于已分配大小,并进行缩容,不需要多余的内存 | |
if (list_resize(self, Py_SIZE(self)) < 0)  | |
goto error;  | |
    } | |
Py_DECREF(it);  | |
    Py_RETURN_NONE; | |
  error: | |
Py_DECREF(it);  | |
return NULL;  | |
} | |
static int  | |
app1(PyListObject *self, PyObject *v)  | |
{ | |
Py_ssize_t n = PyList_GET_SIZE(self); // 获取 list 对象大小  | |
assert (v != NULL);  | |
if (n == PY_SSIZE_T_MAX) {  | |
PyErr_SetString(PyExc_OverflowError,  | |
"cannot add more objects to list");  | |
return -1;  | |
    } | |
    // 扩容操作,self 是列表,n+1 就是 newsize, 或者说新的 ob_size | |
    // 里面有很重要的一步,就是将列表的 ob_size 设置为 newsize, 也就是 n+1 | |
    // 因为 append 之后列表长度大小会变化,而 ob_size 则时刻维护着这个大小 | |
if (list_resize(self, n+1) < 0)  | |
	// 是否需要扩容 | |
return -1;  | |
Py_INCREF(v);  | |
PyList_SET_ITEM(self, n, v); // 根据大小 n 作为下标设置元素的位置  | |
return 0;  | |
} | |
PyObject * | |
PySequence_Fast(PyObject *v, const char *m)  | |
{ | |
    //v 为传递进来的 iterable 对象 | |
PyObject *it;  | |
if (v == NULL) {  | |
	// 为空则抛异常 | |
return null_error();  | |
    } | |
if (PyList_CheckExact(v) || PyTuple_CheckExact(v)) {  | |
	// 校验是否为 list 或者为 typle, 如果满足条件,则直接返回该对象,否则往下走,创建一个空 list, 再调用 list_extend | |
Py_INCREF(v);  | |
return v;  | |
    } | |
it = PyObject_GetIter(v); // 从 iterable 对象中获取__iter__方法,调用返回 iterator  | |
if (it == NULL) {  | |
	// 如果返回了空,抛异常 | |
if (PyErr_ExceptionMatches(PyExc_TypeError))  | |
PyErr_SetString(PyExc_TypeError, m);  | |
return NULL;  | |
    } | |
v = PySequence_List(it); // 创建一个 list 对象  | |
Py_DECREF(it);  | |
return v;  | |
} | |
PyObject * | |
PySequence_List(PyObject *v)  | |
{ | |
PyObject *result; /* result list */  | |
PyObject *rv; /* return value from PyList_Extend */  | |
if (v == NULL) {  | |
return null_error();  | |
    } | |
result = PyList_New(0); // 创建 list,大小为 0  | |
if (result == NULL)  | |
return NULL;  | |
rv = _PyList_Extend((PyListObject *)result, v); // 里面调用的就是 extend 方法  | |
if (rv == NULL) {  | |
Py_DECREF(result);  | |
return NULL;  | |
    } | |
Py_DECREF(rv);  | |
return result;  | |
} | |
PyObject * | |
_PyList_Extend(PyListObject *self, PyObject *iterable)  | |
{ | |
return list_extend(self, iterable);  | |
} | 
list_extend 做的事情其实并不复杂,主要先判断你传递的参数是否为 list 对象或者 tuple 对象,如果满足,则将该对象里的每个元素通过 for 循环一个一个设置到列表里,如果不是判断不是 list、tuple 对象,就会尝试获取该对象的__iter__方法,并 new 一个空列表,再通过调用 list_extend 重新走一遍;如果你一开始传递的参数就不是 list 或者 tuple,它就会直接获取该对象的__iter__方法,调用获取 iterator,然后获取 iterator 的__next__方法,并通过 for 死循环不断调用__next__获取元素并设置到列表,中间会一直判断是否需要扩容操作,最后还会判断是否需要进行缩容操作
# 列表扩容操作
创建 list 时,app1 方法在每次设置元素时都会调用 list_resize 判断是否需要扩容,现在来看看具体实现
static int  | |
list_resize(PyListObject *self, Py_ssize_t newsize)  | |
{ | |
    //self 就是列表,newsize 就是指元素在添加之后的 ob_size | |
    // 比如列表当前 ob_size 为 5,添加一个元素之后为 6,发现容量不够,就会扩容,那么 newsize 就为 6 | |
PyObject **items; // 二级指针,用来指向指针数组的  | |
size_t new_allocated, num_allocated_bytes; // 新的容量、对应的内存大小  | |
Py_ssize_t allocated = self->allocated; // 获取原来的容量  | |
    /* Bypass realloc() when a previous overallocation is large enough | |
to accommodate the newsize. If the newsize falls lower than half  | |
the allocated size, then proceed with the realloc() to shrink the list.  | |
*/  | |
    // 如果 newsize 达到容量的一般,但还没有超过容量,那么意味着 newsize、或者新的 ob_size 和容量是匹配的,不需要扩容 | |
if (allocated >= newsize && newsize >= (allocated >> 1)) {  | |
assert(self->ob_item != NULL || newsize == 0);  | |
Py_SET_SIZE(self, newsize); // 只需要将列表的 ob_size 设置为 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().  | |
* Add padding to make the allocated size multiple of 4.  | |
* The growth pattern is: 0, 4, 8, 16, 24, 32, 40, 52, 64, 76, ...  | |
* 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.  | |
*/  | |
    // 走到这,说明容量与 ob_size 不匹配了,需要进行扩容或者缩容 | |
    // 需要申请新的底层数组,具体申请多少个,这个是公式 | |
new_allocated = ((size_t)newsize + (newsize >> 3) + 6) & ~(size_t)3;  | |
    /* Do not overallocate if the new size is closer to overalocated size | |
* than to the old size.  | |
*/  | |
if (newsize - Py_SIZE(self) > (Py_ssize_t)(new_allocated - newsize))  | |
new_allocated = ((size_t)newsize + 3) & ~(size_t)3;  | |
if (newsize == 0) // 如果 newsize 为 0,那么容量也会变成 0,假设将列表全部清空了,容量就会变为 0  | |
new_allocated = 0;  | |
num_allocated_bytes = new_allocated * sizeof(PyObject *); // 在 list 数组中,存放的都是 PyObject *,所以要计算内存  | |
    // 调用 C malloc 申请相应大小的内存,将其指针交给 items | |
items = (PyObject **)PyMem_Realloc(self->ob_item, num_allocated_bytes);  | |
if (items == NULL) {  | |
	// 申请内存失败,没有内存了 | |
PyErr_NoMemory();  | |
return -1;  | |
    } | |
self->ob_item = items; // 指向新的数组,此时列表就发生了扩容或缩容  | |
Py_SET_SIZE(self, newsize); // 设置列表的 ob_size 为 newsize  | |
self->allocated = new_allocated; // 设置新的容量大小  | |
return 0;  | |
} | 
The growth pattern is: 0, 4, 8, 16, 24, 32, 40, 52, 64, 76, …,当发现容量不够时才会开始扩容
newsize = 0  | |
ob_size = 0  | |
allocated = 0  | |
for i in range(100):  | |
newsize += 1  | |
if (allocated >= newsize and newsize >= (allocated >> 1)):  | |
        ob_size = newsize | |
        continue | |
new_allocated = (newsize + (newsize >> 3) + 6) & ~3  | |
print("new allocated: ", new_allocated)  | |
if (newsize - ob_size > (new_allocated - newsize)):  | |
new_allocated = (newsize + 3) & ~3  | |
print("update new allocated: ", new_allocated)  | |
    ob_size = newsize | |
    allocated = new_allocated | |
=====================> 打印结果  | |
new allocated: 4  | |
new allocated: 8  | |
new allocated: 16  | |
new allocated: 24  | |
new allocated: 32  | |
new allocated: 40  | |
new allocated: 52  | |
new allocated: 64  | |
new allocated: 76  | |
new allocated: 92  | |
new allocated: 108  | 
不断往空列表中 append 元素时,容量就会按上面的规律变化
lst = []  | |
allocated = 0  | |
for item in range(100):  | |
lst.append(item)  | |
    # 计算 ob_size | |
ob_size = len(lst)  | |
if ob_size > allocated:  | |
allocated = (lst.__sizeof__() - [].__sizeof__()) // 8  | |
print("new allocated: ", allocated, "ob size: ", lst.__sizeof__() - [].__sizeof__())  | |
=====================> 打印结果  | |
new allocated: 4 ob size: 32  | |
new allocated: 8 ob size: 64  | |
new allocated: 16 ob size: 128  | |
new allocated: 24 ob size: 192  | |
new allocated: 32 ob size: 256  | |
new allocated: 40 ob size: 320  | |
new allocated: 52 ob size: 416  | |
new allocated: 64 ob size: 512  | |
new allocated: 76 ob size: 608  | |
new allocated: 92 ob size: 736  | |
new allocated: 108 ob size: 864  | 
如果直接通过 lst = [] 这种方式创建列表,那么其长度和容量是一样的
lst = [0] * 1000  | |
# 长度与容量是一样的 | |
print(len(lst), (lst.__sizeof__() - [].__sizeof__()) // 8)  | |
# 如果此时添加一个元素,那么 ob_size 就会变为 1001, 大于容量 1000 | |
# 此时列表会进行扩容,按照计算公式进行变化 | |
# new_allocated = ((size_t)newsize + (newsize >> 3) + 6) & ~(size_t)3; | |
print("新容量: ", (1001 + (1001 >> 3) + 6) & ~3) # 新容量: 1132  | |
# 添加一个元素 | |
lst.append(0)  | |
# 计算容量,结果是一样的 | |
print((lst.__sizeof__() - [].__sizeof__()) // 8) # 1132  | 
了解完扩容,还有一个缩容的情况,假如在扩容操作时,发现长度没有超过容量但又达到容量的一半,是不会缩容的,如果发现空闲了 8 个位置,但只需要两个,那么此时就会进行缩容操作
lst = [0] * 1000  | |
# 长度与容量是一样的 | |
print(len(lst), (lst.__sizeof__() - [].__sizeof__()) // 8) # 1000:1000  | |
# 删除 500 个元素,此时 ob_size 为 500 | |
lst[:500] = []  | |
# 此时容量达到容量一半,不会缩容 | |
print(len(lst), (lst.__sizeof__() - [].__sizeof__()) // 8) # 1000:1000  | |
# 如果再删除一个元素,那么此时就会进行缩容操作 ob_size 变成 499, 小于 1000 // 2 | |
# 缩容公式还是一样的: new_allocated = ((size_t) newsize + (newsize >> 3) + 6) & ~(size_t) 3; | |
print((499 + (499 >> 3) + 6) & ~3) # 564  | |
_ = lst.pop()  | |
print(len(lst), (lst.__sizeof__() - [].__sizeof__()) // 8) # 长度 499 容量 564  | 
此外还有一个判断为 if (newsize == 0),如果 newsize 为 0,那么容量也是 0
lst = [0] * 1000  | |
print(len(lst), (lst.__sizeof__() - [].__sizeof__()) // 8) # 1000:1000  | |
lst[:] = []  | |
print(len(lst), (lst.__sizeof__() - [].__sizeof__()) // 8) # 0:0  | |
print((0 + (0 >> 3) + 6) & ~3) # 4  | 
当发现 newsize 为 0 时,就会把容量设置为 0,CPython 认为列表长度为 0 的话,可能你并不想使用这个列表
# 获取元素
我们在使用列表时,可以通过下标:lst [1] 这种方式获取元素,那么底层是怎么实现的?
PyListType 里定义了映射协议结构
static PyMappingMethods list_as_mapping = {  | |
(lenfunc)list_length,  | |
(binaryfunc)list_subscript,  | |
(objobjargproc)list_ass_subscript  | |
};  | 
通过下标获取元素时会触发 list_subscript 方法
static PyObject *  | |
list_subscript(PyListObject* self, PyObject* item)  | |
{ | |
    // 先判断 item 是不是一个整数,item 除了整数之外,也可以是切片 | |
if (_PyIndex_Check(item)) {  | |
        Py_ssize_t i; | |
	// 检测 i 是否合法,因为 Python 的整型没有限制,列表长度和容量是由具体类型的变量维护的,这个数是有范围的 | |
	// 如果输入一个过大的数字,Python 会报错:IndexError: cannot fit 'int' into an index-sized integer | |
i = PyNumber_AsSsize_t(item, PyExc_IndexError);  | |
if (i == -1 && PyErr_Occurred()) // 设置异常  | |
return NULL;  | |
if (i < 0)  | |
	    // 说明索引是负数,那么加上列表长度就变为正数索引了 | |
i += PyList_GET_SIZE(self);  | |
return list_item(self, i); // 调用 list_item 通过 ob_item [i] 获取元素,方法在下边  | |
    } | |
else if (PySlice_Check(item)) {  | |
	// 走到这说明是一个切片 | |
	//start: 切片起始位置、stop: 切片结束位置、step:步长 | |
	//slicelength: 获取元素个数,比如 [1:5:2], 那么就为 2, 因为只能获取索引为 1 和 3 的元素 | |
	//i: 循环变量,因为切片只能循环获取每一个元素,比如 [1:5:2], 需要循环两次,第一次 cur 为 1, 第二次 cur 为 3 | |
Py_ssize_t start, stop, step, slicelength, i;  | |
size_t cur; // 底层数组中元素的索引  | |
PyObject* result; // 返回结果  | |
PyObject* it;  | |
PyObject **src, **dest;  | |
if (PySlice_Unpack(item, &start, &stop, &step) < 0) {  | |
	    // 对切片 item 进行解包,得到起始、结束、步长 | |
return NULL;  | |
        } | |
	// 计算 slicelength, 我记得前面的文章好像分析过 PySlice_AdjustIndices 里面的源码,是不是 range 源码解析❓ | |
slicelength = PySlice_AdjustIndices(Py_SIZE(self), &start, &stop,  | |
step);  | |
	// 如果 slicelength 为 0, 说明没有元素可以获取,因此直接返回一个空列表即可 | |
if (slicelength <= 0) {  | |
return PyList_New(0);  | |
        } | |
	// 如果步长为 1, 那么在 list_slice 内部会创建一个 PyListObject *np, 申请相应的底层数组,设置 allocated | |
	//allocated 为 stop-start, 然后将参数列表中索引为 ilow 的元素到索引为 ihigh 的元素依次拷贝到 np->ob_item, 然后设置 ob_size 并返回 | |
else if (step == 1) {  | |
return list_slice(self, start, stop);  | |
        } | |
else {  | |
	    // 走到这,说明步长不为 1, result 为 PyListObject *, 底层数组没有存储在 PyListObject 中,而是通过 ob_item 关联 | |
	    //list_new_prealloc 就是申请底层数组、设置容量,这里容量为 slicelength | |
result = list_new_prealloc(slicelength);  | |
if (!result) return NULL;  | |
src = self->ob_item; //src 为二级指针  | |
dest = ((PyListObject *)result)->ob_item; // 同理 src  | |
	    // 进行循环操作,cur 从 start 开始遍历,每次加上 step 步长 | |
for (cur = start, i = 0; i < slicelength;  | |
cur += (size_t)step, i++) {  | |
it = src[cur]; //it 就是 self->ob_item 中的元素  | |
Py_INCREF(it); // 获取元素的对象引用计数 + 1  | |
dest[i] = it; // 将获取元素设置到 dest  | |
            } | |
Py_SET_SIZE(result, slicelength); // 将大小设置为 slicelength, 说明通过切片创建新列表,其长度和容量也是一致的  | |
return result; // 返回结果  | |
        } | |
    } | |
else {  | |
	//item 不合法 | |
PyErr_Format(PyExc_TypeError,  | |
"list indices must be integers or slices, not %.200s",  | |
Py_TYPE(item)->tp_name);  | |
return NULL;  | |
    } | |
} | |
static PyObject *  | |
list_item(PyListObject *a, Py_ssize_t i)  | |
{ | |
    // 检测索引 i 的合法性,如果 i > 列表长度,那么会报索引越界错误 | |
if (!valid_index(i, Py_SIZE(a))) {  | |
	// 如果索引为负数也会报索引越界错误,因为上面已经对负数做了处理了,如果还是一个负数,那么同样报错 | |
	// 假设列表长度为 3, 索引为 - 100, 加上长度后是 - 97, 结果还是负数,所以还是会报错 | |
if (indexerr == NULL) {  | |
indexerr = PyUnicode_FromString(  | |
"list index out of range");  | |
if (indexerr == NULL)  | |
return NULL;  | |
        } | |
PyErr_SetObject(PyExc_IndexError, indexerr);  | |
return NULL;  | |
    } | |
Py_INCREF(a->ob_item[i]); // 引用计数 + 1  | |
return a->ob_item[i]; // 通过下标获取第 i 个元素  | |
} | 
分析过后,其实列表的切片操作也不是什么神奇的操作,实现的逻辑也比较简单
# 创建 PyListObject
创建一个列表,在 Python 底层只提供了唯一一个 Python/C API,此方法接收一个 size 参数,允许我们在创建一个 PyListObject 时,指定底层数组的长度
PyObject * | |
PyList_New(Py_ssize_t size)  | |
{ | |
PyListObject *op; // 声明一个 PyListObject * 对象  | |
if (size < 0) {  | |
	//size 大小小于 0, 抛异常 | |
PyErr_BadInternalCall();  | |
return NULL;  | |
    } | |
    // 缓存池是否可用 | |
if (numfree) {  | |
	// 将缓存池内对象个数 - 1 | |
numfree--;  | |
	// 从缓存池中获取一个 | |
op = free_list[numfree];  | |
	// 设置引用计数 | |
_Py_NewReference((PyObject *)op);  | |
} else {  | |
	// 缓存池不可用时,就申请内存 | |
op = PyObject_GC_New(PyListObject, &PyList_Type);  | |
if (op == NULL) // 申请失败  | |
return NULL;  | |
    } | |
if (size <= 0)  | |
	// 如果 size 小于等于 0, ob_item 就设置为 NULL | |
op->ob_item = NULL;  | |
else {  | |
	// 否则,创建一个指定容量的指针数组,然后让 ob_item 指向它 | |
	// 所以是先创建 PyListObject 对象,然后创建底层数组,最后通过 ob_item 关联 | |
op->ob_item = (PyObject **) PyMem_Calloc(size, sizeof(PyObject *));  | |
if (op->ob_item == NULL) {  | |
Py_DECREF(op);  | |
return PyErr_NoMemory();  | |
        } | |
    } | |
Py_SET_SIZE(op, size); // 设置 ob_size  | |
op->allocated = size; // 设置 allocated  | |
_PyObject_GC_TRACK(op); // GC 管理  | |
return (PyObject *) op;  | |
} | 
在创建 PyListObject 对象时,会先检测缓存池是否可用,有就直接拿来用,否则通过 malloc 在系统堆上申请,缓存池最多维护 80 个 PyListObject 对象
/* Empty list reuse scheme to save calls to malloc and free */ | |
#ifndef PyList_MAXFREELIST | |
#  define PyList_MAXFREELIST 80 | |
#endif | |
static PyListObject *free_list[PyList_MAXFREELIST];  | 
既然能从缓存池中获取,那么在执行析构函数时,也要把列表放到缓存池中
static void  | |
list_dealloc(PyListObject *op)  | |
{ | |
    Py_ssize_t i; | |
PyObject_GC_UnTrack(op);  | |
Py_TRASHCAN_BEGIN(op, list_dealloc)  | |
if (op->ob_item != NULL) { // 底层数组不为空时  | |
        /* Do it backwards, for Christian Tismer. | |
There's a simple test case where somehow this reduces  | |
thrashing when a *very* large list is created and  | |
immediately deleted. */  | |
i = Py_SIZE(op);  | |
while (--i >= 0) {  | |
	    // 将底层数组中每个指针指向的对象的引用计数都 - 1 | |
Py_XDECREF(op->ob_item[i]);  | |
        } | |
PyMem_FREE(op->ob_item); // 释放底层数组所占的内存  | |
    } | |
    // 判断缓存池里面 PyLitObject 对象的个数,如果没满,就添加到缓存池 | |
if (numfree < PyList_MAXFREELIST && PyList_CheckExact(op))  | |
free_list[numfree++] = op;  | |
    else | |
	// 否则缓存池满了,就将 PyListObject 对象所占的内存释放 | |
Py_TYPE(op)->tp_free((PyObject *)op);  | |
Py_TRASHCAN_END  | |
} | 
在创建一个 PyListObject 对象时,实际上分为了两步,先创建 PyListObject 对象,然后创建底层数组,最后让 PyListObject 对象中的 ob_item 指向这个底层数组,那么销毁一个 PyListObject 对象时,也就先销毁 ob_item 维护的底层数组,然后再释放 PyListObject 对象本身(在缓存池已满的情况下)

可以看到,b 对象与 a 对象的地址是一致的
还有一些方法,有需要可以自己去查看源码了解一下,我懒得看了😂
static PyMethodDef list_methods[] = {  | |
{"__getitem__", (PyCFunction)list_subscript, METH_O|METH_COEXIST, "x.__getitem__(y) <==> x[y]"},  | |
LIST___REVERSED___METHODDEF  | |
LIST___SIZEOF___METHODDEF  | |
LIST_CLEAR_METHODDEF  | |
LIST_COPY_METHODDEF  | |
LIST_APPEND_METHODDEF  | |
LIST_INSERT_METHODDEF  | |
LIST_EXTEND_METHODDEF  | |
LIST_POP_METHODDEF  | |
LIST_REMOVE_METHODDEF  | |
LIST_INDEX_METHODDEF  | |
LIST_COUNT_METHODDEF  | |
LIST_REVERSE_METHODDEF  | |
LIST_SORT_METHODDEF  | |
{"__class_getitem__", (PyCFunction)Py_GenericAlias, METH_O|METH_CLASS, PyDoc_STR("See PEP 585")},  | |
{NULL, NULL} /* sentinel */  | |
};  |