08月15, 2020

17. 向 List Object append 和 pop 元素

append

向 List Object 任意位置插入数据,只要插入位置不是结尾,那么就需要将插入位置之后的元素后移,因此插入数据的时间复杂度为 O(n)。向数据插入数据有一个特例位置,向尾部追加 append 一个元素。

Python 的实现非常简单:

static int
app1(PyListObject *self, PyObject *v)
{
    Py_ssize_t n = PyList_GET_SIZE(self);

    // 省略了错误处理代码
    if (list_resize(self, n+1) < 0)
        return -1;

    Py_INCREF(v);
    PyList_SET_ITEM(self, n, v);
    return 0;
}

只有两步:重置 List 空间;新增元素设置到尾部元素。非常直接。

img

pop

List 通过 pop 方法弹出元素。具体实现:

//Objects/listobject.c:1025
static PyObject *
list_pop_impl(PyListObject *self, Py_ssize_t index)
/*[clinic end generated code: output=6bd69dcb3f17eca8 input=b83675976f329e6f]*/
{
    PyObject *v;
    int status;

    // 忽略一些无关代码
    v = self->ob_item[index];

    // 如果要弹出结尾元素,快捷处理
    if (index == Py_SIZE(self) - 1) {
        status = list_resize(self, Py_SIZE(self) - 1);
        if (status >= 0)
            return v; /* and v now owns the reference the list had */
        else
            return NULL;
    }

    // 如果位于 List 中间,则删除元素
    Py_INCREF(v);
    status = list_ass_slice(self, index, index+1, (PyObject *)NULL);
    if (status < 0) {
        Py_DECREF(v);
        return NULL;
    }
    return v;
}

pop 元素根据弹出元素是否是尾部元素区别处理,如果是尾部元素,那么只是进行 resize 操作,弹出元素会被直接截断抛弃。实际上在这个过程中有可能根本不会发生真正的 resize,只是调整了 ob_size 的大小。

image.png

较为复杂的情况是弹出元素在数组中间或者头部,当目标元素弹出后,会在 List 中出现一个 “空缺”,其后的元素会被 “前移”,这与插入过程恰好相反。

img

本文链接:http://www.thinkinpython.com/post/deep_python_vm_17.html

-- EOF --