简体   繁体   English

Python的insert()方法与通过切片插入之间的实现和性能差异

[英]Implementation and performance difference between Python's insert() method and inserting by slicing

What is the difference between inserting an element in a python list in the following ways? 通过以下方式将元素插入python列表中有什么区别?

myList.insert(at, myValue)
myList[at:at] = [myValue]

I have run some tests and the performance of the two are very similar, but the slicing insert consistently produces slightly better results. 我已经进行了一些测试,两者的性能非常相似,但是切片插入始终会产生更好的结果。 My question is regarding the difference in implementation and performance, not the behaviour. 我的问题是关于实现和性能的差异,而不是行为。

We have the same behaviour, see bellow: 我们具有相同的行为,请参见下面的内容:

The default behaviour is to insert the item at the given index; 默认行为是将项目插入给定索引; each value at greater index are shifted one position to the end. 索引较高的每个值都移到最后一个位置。

>>> my_list = ['a', 'b', 'c', 'd', 'e', 'f', 'g']
>>> my_list.insert(5, 'item')
>>> my_list
['a', 'b', 'c', 'd', 'e', 'item', 'f', 'g']

>>> my_list = ['a', 'b', 'c', 'd', 'e', 'f', 'g']
>>> my_list.insert(-3, 'item')
>>> my_list
['a', 'b', 'c', 'd', 'item', 'e', 'f', 'g']

If the list is empty, the item is appended normally. 如果列表为空,则该项目将正常添加。

>>> my_list = []
>>> my_list.insert(5, 'item')
>>> my_list
['item']

>>> my_list = []
>>> my_list.insert(-3, 'item')
>>> my_list
['item']

If the index is out of bounds, the item is appended to the end if the index is positive or to the beginning if negative. 如果索引超出范围,则如果索引为正,则该项目将追加到末尾;如果索引为负,则将其追加到开头。 No exception is raised. 没有例外。

>>> my_list = ['a', 'b']
>>> my_list.insert(5, 'item')
>>> my_list
['a', 'b', 'item']

>>> my_list = ['a', 'b']
>>> my_list.insert(-3, 'item')
>>> my_list
['item', 'a', 'b']

We have exactly the same behaviour with slice notation, in the case of a range of same indexes: 在一系列相同的索引的情况下,我们使用切片符号具有完全相同的行为:

>>> my_list = ['a', 'b', 'c', 'd', 'e', 'f', 'g']
>>> my_list[5:5] = ['item']
>>> my_list
['a', 'b', 'c', 'd', 'e', 'item', 'f', 'g']

>>> my_list = ['a', 'b', 'c', 'd', 'e', 'f', 'g']
>>> my_list[-3:-3] = ['item']
>>> my_list
['a', 'b', 'c', 'd', 'item', 'e', 'f', 'g']

>>> my_list = []
>>> my_list[5:5] = ['item']
>>> my_list
['item']

>>> my_list = []
>>> my_list[-3:-3] = ['item']
>>> my_list
['item']

>>> my_list = ['a', 'b']
>>> my_list[5:5] = ['item']
>>> my_list
['a', 'b', 'item']

>>> my_list = ['a', 'b']
>>> my_list[-3:-3] = ['item']
>>> my_list
['item', 'a', 'b']

The slice notation is the same as calling __setitem__() method with a slice object: 切片符号与使用slice对象调用__setitem__()方法相同:

>>> my_list = ['a', 'b', 'c', 'd', 'e', 'f', 'g']
>>> my_list.__setitem__(slice(5, 5), ['item'])
>>> my_list
['a', 'b', 'c', 'd', 'e', 'item', 'f', 'g']

Implementation details 实施细节

According to the CPython implementation available on GitHub at https://github.com/python/cpython/blob/master/Objects/listobject.c and https://github.com/python/cpython/blob/master/Objects/listobject.c , we have: 根据GitHub上提供的CPython实现, 网址https://github.com/python/cpython/blob/master/Objects/listobject.chttps://github.com/python/cpython/blob/master/Objects/listobject .c ,我们有:

The insert() method is defined in the following function: insert()方法在以下函数中定义:

static PyObject *
listinsert(PyListObject *self, PyObject *args)
{
    Py_ssize_t i;
    PyObject *v;
    if (!PyArg_ParseTuple(args, "nO:insert", &i, &v))
        return NULL;
    if (ins1(self, i, v) == 0)
        Py_RETURN_NONE;
    return NULL;
}

Which calls ins1() function, here is the C code: 调用ins1()函数,这里是C代码:

static int
ins1(PyListObject *self, Py_ssize_t where, PyObject *v)
{
    Py_ssize_t i, n = Py_SIZE(self);
    PyObject **items;
    if (v == NULL) {
        PyErr_BadInternalCall();
        return -1;
    }
    if (n == PY_SSIZE_T_MAX) {
        PyErr_SetString(PyExc_OverflowError,
            "cannot add more objects to list");
        return -1;
    }

    if (list_resize(self, n+1) < 0)
        return -1;

    if (where < 0) {
        where += n;
        if (where < 0)
            where = 0;
    }
    if (where > n)
        where = n;
    items = self->ob_item;
    for (i = n; --i >= where; )
        items[i+1] = items[i];
    Py_INCREF(v);
    items[where] = v;
    return 0;
}

The slice call is done by the PyList_SetSlice() function: 切片调用由PyList_SetSlice()函数完成:

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 optimised implementation is done in: 优化的实现方式是:

static int
list_ass_slice(PyListObject *a, Py_ssize_t ilow, Py_ssize_t ihigh, PyObject *v)

The insertion is done in the following piece of code: 插入通过以下代码完成:

else if (d > 0) { /* Insert d items */
    k = Py_SIZE(a);
    if (list_resize(a, k+d) < 0)
        goto Error;
    item = a->ob_item;
    memmove(&item[ihigh+d], &item[ihigh],
        (k - ihigh)*sizeof(PyObject *));
}
for (k = 0; k < n; k++, ilow++) {
    PyObject *w = vitem[k];
    Py_XINCREF(w);
    item[ilow] = w;
}

Hope it helps! 希望能帮助到你!

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM