通过源码分析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上的解答,发现源码还是最好的解释。上述关键位置已经加了注释,应该很好理解。
总结
以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作能带来一定的帮助,如果有疑问大家可以留言交流,谢谢大家对毛票票的支持。