Slice assignment in Python through source code analysis
- 2020-06-01 10:11:24
- OfStack
This paper mainly introduces the relevant content of Python slice assignment, which is Shared for your reference and learning. The following is a detailed introduction:
Yesterday, a student asked me this question:
t = [1, 2, 3]
t[1:1] = [7] # Thank you for @1 Went ahead The question was written as t[1:1] = 7 the
print t # The output [1, 7, 2, 3]
This is a problem that I haven't really seen before. Would anyone assign a list like this? However, the reason for this output is really worth further understanding, after all, we have seen the source code analysis of Python before. (aside: it is said that a new version has been written recently.)
Think today free to look at the source of Python, to understand what the principle is.
Note: I downloaded the code of Python 2.7.6 before I came here, and I looked at it directly.
There is one PyList_SetSlice function in Objects/ listobject.c, which reads:
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);
}
The useful first sentence is list_ass_slice, so take a look at the code for this function:
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 Note:
The length to assign 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(" The key point \n");
/*
the5fire Note:
the list After corresponding slices 1 The size assigned by moving back everything after the value of the bit
According to the above python So here's the code
The principle of t:
|1|2|3|
Move backward 1 A, because len([7]) = 1
|1| empty |2|3| Shift the last two
*/
memmove(&item[ihigh+d], &item[ihigh],
(k - ihigh)*sizeof(PyObject *));
}
/*
the5fire Note:
The assignment operation, that is, put [7] Assign values to the t In the corresponding position in
ilow is 1, n is 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
}
Read zhihu, stackoverflow solution, found the source code or the best explanation. The above key points have been commented and should be easy to understand.
conclusion