2011-03-03 88 views
2

如果我有一個列表:python如何擴展工作?

a = [1,2,3,4]

,然後使用加4元擴大

a.extend(range(5,10))

我得到

a = [1, 2, 3, 4, 5, 6, 7, 8, 9]

Python是怎樣做到這一點?它是否會創建一個新列表並將這些元素複製到對象上,還是使它變得更大?只關心使用擴展會吞噬內存。我'還問,因爲在一些代碼,我修改,通過10000×100延伸比的1000000

+7

依賴於實現。 – 2011-03-03 04:32:32

+0

感謝您的信息傢伙。 – Martlark 2011-03-03 05:44:05

回答

2

L.extend(M)將攤銷爲O(n),其中n = LEN(米),因此,過度的,不能複製,通常的問題。 可能是一個問題的時間是當沒有足夠的空間擴展到,所以執行副本。當列表很大時,這是一個問題,並且您對單個擴展調用可接受的時間有限制。

這是您應該爲您的問題尋找更高效的數據結構的關鍵。我發現這在實踐中很少是問題。

這裏是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

Python的documentation on it一塊做這件事更快評論說:

延長通過在給定列表中追加所有 項目;相當於 a[len(a):] = L

至於「如何」它在背景後面,你真的不需要關心它。

2

它的工作原理就好像這樣

def extend(lst, iterable): 
    for x in iterable: 
     lst.append(x) 

這個變異的列表中定義的,它不創建一個副本。

根據底層的實現,appendextend可能會觸發列表來複制自己的數據結構,但這是正常的,沒有什麼可擔心的。例如,基於數組的實現通常會以指數形式增長底層數組,並且需要在它們這樣做時複製元素列表。

0

python如何做到這一點?它是否會創建一個新列表並將這些元素複製到對象上,還是使它變得更大?

>>> 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 
>>> 
0

它不創建一個新的列表對象,它擴展a多餘的空間分配。這是不言而喻的,因爲你不做任何分配。 Python不會神奇地用其他對象替換你的對象。:-)

如何在列表對象內部發生內存分配取決於實現。