Python中对切片赋值原理分析

Python (204) 2023-06-03 14:36:13

有这么个问题::

t=[1,2,3]
t[1:1]=[7]
printt#输出[1,7,2,3]

谁会对列表这么进行赋值呢?但是对于这个输出结果的原因确实值得去再了解下,今天看看Python的源码,了解下原理是什么。

注:本地下载的是Python2.7.6的代码,直接看这个。

在Objects/listobject.c中有一个 PyList_SetSlice 函数,是这么写的::

int
PyList_SetSlice(PyObject*a,Py_ssize_tilow,Py_ssize_tihigh,PyObject*v)
{
if(!PyList_Check(a)){
PyErr_BadInternalCall();
return-1;
}
returnlist_ass_slice((PyListObject*)a,ilow,ihigh,v);
}

有用的一句就是 list_ass_slice ,那么再来看看这个函数的代码::

staticint
list_ass_slice(PyListObject*a,Py_ssize_tilow,Py_ssize_tihigh,PyObject*v)
{
/*Because[X]DECREFcanrecursivelyinvokelistoperationson
thislist,wemustpostponeall[X]DECREFactivityuntil
afterthelistisbackinitscanonicalshape.Therefore
wemustallocateanadditionalarray,'recycle',intowhich
wetemporarilycopytheitemsthataredeletedfromthe
list.:-(*/
PyObject*recycle_on_stack[8];
PyObject**recycle=recycle_on_stack;/*willallocatemoreifneeded*/
PyObject**item;
PyObject**vitem=NULL;
PyObject*v_as_SF=NULL;/*PySequence_Fast(v)*/
Py_ssize_tn;/*#ofelementsinreplacementlist*/
Py_ssize_tnorig;/*#ofelementsinlistgettingreplaced*/
Py_ssize_td;/*Changeinsize*/
Py_ssize_tk;
size_ts;
intresult=-1;/*guiltyuntilprovedinnocent*/
#defineb((PyListObject*)v)
if(v==NULL)
n=0;
else{
if(a==b){
/*Specialcase"a[i:j]=a"--copybfirst*/
v=list_slice(b,0,Py_SIZE(b));
if(v==NULL)
returnresult;
result=list_ass_slice(a,ilow,ihigh,v);
Py_DECREF(v);
returnresult;
}
v_as_SF=PySequence_Fast(v,"canonlyassignaniterable");
if(v_as_SF==NULL)
gotoError;
/*
要赋值的长度n
*/
n=PySequence_Fast_GET_SIZE(v_as_SF);
vitem=PySequence_Fast_ITEMS(v_as_SF);
}
if(ilow<0)
ilow=0;
elseif(ilow>Py_SIZE(a))
ilow=Py_SIZE(a);
if(ihigh<ilow)
ihigh=ilow;
elseif(ihigh>Py_SIZE(a))
ihigh=Py_SIZE(a);
norig=ihigh-ilow;
assert(norig>=0);
d=n-norig;
if(Py_SIZE(a)+d==0){
Py_XDECREF(v_as_SF);
returnlist_clear(a);
}
item=a->ob_item;
/*recycletheitemsthatweareabouttoremove*/
s=norig*sizeof(PyObject*);
if(s>sizeof(recycle_on_stack)){
recycle=(PyObject**)PyMem_MALLOC(s);
if(recycle==NULL){
PyErr_NoMemory();
gotoError;
}
}
memcpy(recycle,&item[ilow],s);
if(d<0){/*Delete-ditems*/
memmove(&item[ihigh+d],&item[ihigh],
(Py_SIZE(a)-ihigh)*sizeof(PyObject*));
list_resize(a,Py_SIZE(a)+d);
item=a->ob_item;
}
elseif(d>0){/*Insertditems*/
k=Py_SIZE(a);
if(list_resize(a,k+d)<0)
gotoError;
item=a->ob_item;
printf("关键点\n");
/*
把list对应切片后一位的值之后的所有内容向后移动所赋值的大小
按照上面的python代码这里就是
原理的t:
|1|2|3|
后移一位,因为len([7])=1
|1|空|2|3|把后两个移位
*/
memmove(&item[ihigh+d],&item[ihigh],
(k-ihigh)*sizeof(PyObject*));
}
/*
赋值操作,即把[7]赋值到t里的对应位置上
ilow是1,n是1
*/
for(k=0;k<n;k++,ilow++){
PyObject*w=vitem[k];
Py_XINCREF(w);
item[ilow]=w;
}
for(k=norig-1;k>=0;--k)
Py_XDECREF(recycle[k]);
result=0;
Error:
if(recycle!=recycle_on_stack)
PyMem_FREE(recycle);
Py_XDECREF(v_as_SF);
returnresult;
#undefb
}

源码内有详细注释,编程问题的研究最好的解释还是源码。

THE END

发表回复