切片一定会新建一个 PyListObject;单个索引只是返回已有对象的引用
>>> import dis >>> dis.dis(lambda: L[0]) 1 0 LOAD_GLOBAL 0 (L) 2 LOAD_CONST 0 (0) 4 BINARY_SUBSCR 6 RETURN_VALUE >>> dis.dis(lambda: L[0:2]) 1 0 LOAD_GLOBAL 0 (L) 2 LOAD_CONST 0 (0) 4 LOAD_CONST 1 (2) 6 BUILD_SLICE 2 8 BINARY_SUBSCR 10 RETURN_VALUE
L[0]
直接把整数 0 当成下标走 BINARY_SUBSCRL[0:2]
先 BUILD_SLICE 生成 slice 对象,再走 BINARY_SUBSCRstatic PyObject * list_subscript(PyListObject *self, PyObject *key) { if (PySlice_Check(key)) { // ① 切片分支 Py_ssize_t start, stop, step, slicelength; if (PySlice_Unpack(key, &start, &stop, &step) < 0) return NULL; slicelength = PySlice_AdjustIndices(Py_SIZE(self), &start, &stop, step); /* 新建一个 list,大小=slicelength */ PyListObject *np = (PyListObject *) PyList_New(slicelength); if (!np) return NULL; /* 逐个 PyList_SET_ITEM 拷贝引用 */ for (Py_ssize_t i = 0; i < slicelength; ++i) { PyObject *v = self->ob_item[start]; Py_INCREF(v); PyList_SET_ITEM(np, i, v); start += step; } return (PyObject *)np; } else { // ② 单索引分支 Py_ssize_t i = PyNumber_AsSsize_t(key, PyExc_IndexError); if (i == -1 && PyErr_Occurred()) return NULL; if (i < 0 || i >= Py_SIZE(self)) { PyErr_SetString(PyExc_IndexError, "list index out of range"); return NULL; } PyObject *v = self->ob_item[i]; Py_INCREF(v); return v; // 只增加一次引用计数,直接返回 } }
CPython 内部实现(Objects/listobject.c)
list_subscript(PyListObject *self, PyObject *key)