自己研究的,非官方文档。有问题的话欢迎大佬纠错哈。
先说结论,会发生列表复制。不过结论只针对python 3.13(cpython解释器)有效,其他版本需要另外测试。
可以先测试一下
>>> def perf():
... list1 = [i for i in range(10 * 10000)]
... st1 = time.time()
... list2 = list1[:10 * 9000]
... st2 = time.time()
... print(st2 - st1)
>>> perf()
0.0005638599395751953
下标分别乘10倍(10*10000*10,10*9000*10):0.007214069366455078
再乘10倍:0.07207250595092773
同理测试for in:
0.0019168853759765625
0.021228313446044922
0.19841718673706055
从现象推测,两个切片操作都是会发生内存复制的。
再看看python是怎么编译的。
对于list2 = list1[:9000000]
4 LOAD_FAST 1 (list1)
LOAD_CONST 0 (None)
LOAD_CONST 2 (9000000)
BINARY_SLICE
对于for in
4 LOAD_FAST 1 (list1)
LOAD_CONST 0 (None)
LOAD_CONST 2 (9000000)
BINARY_SLICE
GET_ITER
L5: FOR_ITER 3 (to L6)
python3.13源码中对BINARY_SLICE的执行是:
PyObject *slice = _PyBuildSlice_ConsumeRefs(start, stop);
//....
res = PyObject_GetItem(container, slice);
//....
(注意这段只对python3.13有效,比如官方文档指出BINARY_SLICE是3.12加入的,而cpython main上的BINARY_SLIICE的执行已经和tag=3.13不一样了)
这个PyObject_GetItem的实现就有些复杂了
//abstract.c
PyObject * PyObject_GetItem(PyObject *o, PyObject *key) {
//...
PyMappingMethods *m = Py_TYPE(o)->tp_as_mapping;
if (m && m->mp_subscript) {
PyObject *item = m->mp_subscript(o, key);
//...
}
//列表的定义,listobject.c
PyTypeObject PyList_Type = {
//...
&list_as_mapping, /* tp_as_mapping */
//...
}
static PyMappingMethods list_as_mapping = {
list_length,
list_subscript,
list_ass_subscript
};
//下标的具体实现
static PyObject * list_subscript(PyObject* _self, PyObject* item) {
//...
return return list_slice_wrap(self, start, stop, step);
//...
}
static PyObject * list_slice_wrap(PyListObject *aa, Py_ssize_t start, Py_ssize_t stop, Py_ssize_t step) {
//...
else if (step == 1) {
res = list_slice_lock_held(aa, start, stop);
//...
}
static PyObject * list_slice_lock_held(PyListObject *a, Py_ssize_t ilow, Py_ssize_t ihigh) {
//...
//这里实际发生了新列表list2的内存分配和从list1复制元素的过程
np = (PyListObject *) list_new_prealloc(len);
//...
for (i = 0; i < len; i++) {
PyObject *v = src[i];
dest[i] = Py_NewRef(v);
}
//...
}
【 在 ooolinux 的大作中提到: 】
: list1=[i for i in range(100)]
: for i in list1[:10]:
: print(i,end=' ')
: ...................
--
FROM 223.160.128.*