如果我有一个清单:
a = [1,2,3,4]
然后使用extend
添加4个元素 a.extend(range(5,10))
我得到了
a = [1, 2, 3, 4, 5, 6, 7, 8, 9]
python如何做到这一点?它会创建一个新列表并复制元素,还是会使'a'更大?只关心使用extend会吞噬内存。我也问,因为我正在修改的一些代码中有一条注释,扩展10000 x 100比在1000000的一个块中更快。
答案 0 :(得分:3)
答案 1 :(得分:2)
就像它被定义为
一样def extend(lst, iterable):
for x in iterable:
lst.append(x)
这会改变列表,但不会创建它的副本。
根据底层实现,append
和extend
可能会触发列表来复制自己的数据结构,但这是正常的,无需担心。例如,基于数组的实现通常以指数方式增长底层数组,并且需要在它们这样做时复制元素列表。
答案 2 :(得分:2)
L.extend(M)是摊销的O(n),其中n = len(m),因此过度复制通常不是问题。 可能成为问题的时间是当没有足够的空间扩展时,因此执行复制。当列表很大并且您对单个扩展调用可接受的时间有限制时,这是一个问题。
这是您应该为您的问题寻找更有效的数据结构的关键点。我发现它在实践中很少出现问题。
以下是来自CPython的相关代码,您可以看到在扩展列表时分配了额外的空间以避免过度复制
static PyObject *
listextend(PyListObject *self, PyObject *b)
{
PyObject *it; /* iter(v) */
Py_ssize_t m; /* size of self */
Py_ssize_t n; /* guess for size of b */
Py_ssize_t mn; /* m + n */
Py_ssize_t i;
PyObject *(*iternext)(PyObject *);
/* Special cases:
1) lists and tuples which can use PySequence_Fast ops
2) extending self to self requires making a copy first
*/
if (PyList_CheckExact(b) || PyTuple_CheckExact(b) || (PyObject *)self == b) {
PyObject **src, **dest;
b = PySequence_Fast(b, "argument must be iterable");
if (!b)
return NULL;
n = PySequence_Fast_GET_SIZE(b);
if (n == 0) {
/* short circuit when b is empty */
Py_DECREF(b);
Py_RETURN_NONE;
}
m = Py_SIZE(self);
if (list_resize(self, m + n) == -1) {
Py_DECREF(b);
return NULL;
}
/* note that we may still have self == b here for the
* situation a.extend(a), but the following code works
* in that case too. Just make sure to resize self
* before calling PySequence_Fast_ITEMS.
*/
/* populate the end of self with b's items */
src = PySequence_Fast_ITEMS(b);
dest = self->ob_item + m;
for (i = 0; i < n; i++) {
PyObject *o = src[i];
Py_INCREF(o);
dest[i] = o;
}
Py_DECREF(b);
Py_RETURN_NONE;
}
it = PyObject_GetIter(b);
if (it == NULL)
return NULL;
iternext = *it->ob_type->tp_iternext;
/* Guess a result list size. */
n = _PyObject_LengthHint(b, 8);
if (n == -1) {
Py_DECREF(it);
return NULL;
}
m = Py_SIZE(self);
mn = m + n;
if (mn >= m) {
/* Make room. */
if (list_resize(self, mn) == -1)
goto error;
/* Make the list sane again. */
Py_SIZE(self) = m;
}
/* Else m + n overflowed; on the chance that n lied, and there really
* is enough room, ignore it. If n was telling the truth, we'll
* eventually run out of memory during the loop.
*/
/* Run iterator to exhaustion. */
for (;;) {
PyObject *item = iternext(it);
if (item == NULL) {
if (PyErr_Occurred()) {
if (PyErr_ExceptionMatches(PyExc_StopIteration))
PyErr_Clear();
else
goto error;
}
break;
}
if (Py_SIZE(self) < self->allocated) {
/* steals ref */
PyList_SET_ITEM(self, Py_SIZE(self), item);
++Py_SIZE(self);
}
else {
int status = app1(self, item);
Py_DECREF(item); /* append creates a new ref */
if (status < 0)
goto error;
}
}
/* Cut back result list if initial guess was too large. */
if (Py_SIZE(self) < self->allocated)
list_resize(self, Py_SIZE(self)); /* shrinking can't fail */
Py_DECREF(it);
Py_RETURN_NONE;
error:
Py_DECREF(it);
return NULL;
}
PyObject *
_PyList_Extend(PyListObject *self, PyObject *b)
{
return listextend(self, b);
}
答案 3 :(得分:0)
python如何做到这一点?它会创建一个新列表并复制元素,还是会使'a'更大?
>>> a = ['apples', 'bananas']
>>> b = a
>>> a is b
True
>>> c = ['apples', 'bananas']
>>> a is c
False
>>> a.extend(b)
>>> a
['apples', 'bananas', 'apples', 'bananas']
>>> b
['apples', 'bananas', 'apples', 'bananas']
>>> a is b
True
>>>
答案 4 :(得分:0)
它不会创建新的列表对象,而是扩展a
。这是不言而喻的,因为你没有做出任何分配。 Python不会用其他对象神奇地替换你的对象。 : - )
如何在列表对象中进行内存分配取决于实现。