通過源碼分析Python中的切片賦值
本文主要介紹的關(guān)于Python切片賦值的相關(guān)內(nèi)容,分享出來供大家參考學(xué)習(xí),下面來一起看看詳細(xì)的介紹:
昨天有同學(xué)問了我這么個問題:
t = [1, 2, 3] t[1:1] = [7] # 感謝@一往直前 的疑問,之前寫為 t[1:1] = 7了 print t # 輸出 [1, 7, 2, 3]
這個問題之前還真沒遇到過,有誰會對列表這么進(jìn)行賦值嗎?不過對于這個輸出結(jié)果的原因確實值得去再了解下,畢竟之前也看過《Python源碼分析》。(題外話:據(jù)說最近有大牛在寫新的版本)
想著今天有空看看Python的源碼,去了解下原理是什么。
注:我本地之前下載的是Python2.7.6的代碼,直接看的這個。
在Objects/listobject.c中有一個 PyList_SetSlice 函數(shù),是這么寫的:
int PyList_SetSlice(PyObject *a, Py_ssize_t ilow, Py_ssize_t ihigh, PyObject *v) { if (!PyList_Check(a)) { PyErr_BadInternalCall(); return -1; } return list_ass_slice((PyListObject *)a, ilow, ihigh, v); }
有用的一句就是 list_ass_slice ,那么再來看看這個函數(shù)的代碼:
static int list_ass_slice(PyListObject *a, Py_ssize_t ilow, Py_ssize_t ihigh, PyObject *v) { /* Because [X]DECREF can recursively invoke list operations on this list, we must postpone all [X]DECREF activity until after the list is back in its canonical shape. Therefore we must allocate an additional array, 'recycle', into which we temporarily copy the items that are deleted from the list. :-( */ PyObject *recycle_on_stack[8]; PyObject **recycle = recycle_on_stack; /* will allocate more if needed */ PyObject **item; PyObject **vitem = NULL; PyObject *v_as_SF = NULL; /* PySequence_Fast(v) */ Py_ssize_t n; /* # of elements in replacement list */ Py_ssize_t norig; /* # of elements in list getting replaced */ Py_ssize_t d; /* Change in size */ Py_ssize_t k; size_t s; int result = -1; /* guilty until proved innocent */ #define b ((PyListObject *)v) if (v == NULL) n = 0; else { if (a == b) { /* Special case "a[i:j] = a" -- copy b first */ v = list_slice(b, 0, Py_SIZE(b)); if (v == NULL) return result; result = list_ass_slice(a, ilow, ihigh, v); Py_DECREF(v); return result; } v_as_SF = PySequence_Fast(v, "can only assign an iterable"); if(v_as_SF == NULL) goto Error; /* the5fire注: 要賦值的長度n */ n = PySequence_Fast_GET_SIZE(v_as_SF); vitem = PySequence_Fast_ITEMS(v_as_SF); } if (ilow < 0) ilow = 0; else if (ilow > Py_SIZE(a)) ilow = Py_SIZE(a); if (ihigh < ilow) ihigh = ilow; else if (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); return list_clear(a); } item = a->ob_item; /* recycle the items that we are about to remove */ s = norig * sizeof(PyObject *); if (s > sizeof(recycle_on_stack)) { recycle = (PyObject **)PyMem_MALLOC(s); if (recycle == NULL) { PyErr_NoMemory(); goto Error; } } memcpy(recycle, &item[ilow], s); if (d < 0) { /* Delete -d items */ memmove(&item[ihigh+d], &item[ihigh], (Py_SIZE(a) - ihigh)*sizeof(PyObject *)); list_resize(a, Py_SIZE(a) + d); item = a->ob_item; } else if (d > 0) { /* Insert d items */ k = Py_SIZE(a); if (list_resize(a, k+d) < 0) goto Error; item = a->ob_item; printf("關(guān)鍵點\n"); /* the5fire注: 把list對應(yīng)切片后一位的值之后的所有內(nèi)容向后移動所賦值的大小 按照上面的python代碼這里就是 原理的t: |1|2|3| 后移一位,因為len([7]) = 1 |1|空|2|3|把后兩個移位 */ memmove(&item[ihigh+d], &item[ihigh], (k - ihigh)*sizeof(PyObject *)); } /* the5fire注: 賦值操作,即把[7]賦值到t里的對應(yīng)位置上 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); return result; #undef b }
看了知乎,stackoverflow上的解答,發(fā)現(xiàn)源碼還是最好的解釋。上述關(guān)鍵位置已經(jīng)加了注釋,應(yīng)該很好理解。
總結(jié)
以上就是這篇文章的全部內(nèi)容了,希望本文的內(nèi)容對大家的學(xué)習(xí)或者工作能帶來一定的幫助,如果有疑問大家可以留言交流,謝謝大家對腳本之家的支持。
相關(guān)文章
python?Prophet時間序列預(yù)測工具庫使用功能探索
Python?Prophet是一個強(qiáng)大的時間序列預(yù)測工具,由Facebook開發(fā),具有易用性和高度可定制性的特點,本文將深入介紹Python?Prophet的基本概念、安裝方法以及如何使用它進(jìn)行時間序列預(yù)測,并提供豐富的示例代碼來幫助大家入門2024-01-01