通过源码分析Python中的切片赋值
本文主要介绍的关于Python切片赋值的相关内容,分享出来供大家参考学习,下面来一起看看详细的介绍:
昨天有同学问了我这么个问题:
t=[1,2,3] t[1:1]=[7]#感谢@一往直前的疑问,之前写为t[1:1]=7了 printt#输出[1,7,2,3]
这个问题之前还真没遇到过,有谁会对列表这么进行赋值吗?不过对于这个输出结果的原因确实值得去再了解下,毕竟之前也看过《Python源码分析》。(题外话:据说最近有大牛在写新的版本)
想着今天有空看看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;
/*
the5fire注:
要赋值的长度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(ihighPy_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");
/*
the5fire注:
把list对应切片后一位的值之后的所有内容向后移动所赋值的大小
按照上面的python代码这里就是
原理的t:
|1|2|3|
后移一位,因为len([7])=1
|1|空|2|3|把后两个移位
*/
memmove(&item[ihigh+d],&item[ihigh],
(k-ihigh)*sizeof(PyObject*));
}
/*
the5fire注:
赋值操作,即把[7]赋值到t里的对应位置上
ilow是1,n是1
*/
for(k=0;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
}
看了知乎,stackoverflow上的解答,发现源码还是最好的解释。上述关键位置已经加了注释,应该很好理解。
总结
以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作能带来一定的帮助,如果有疑问大家可以留言交流,谢谢大家对毛票票的支持。