在 python 中,如果 x 是 list,為什麼 x += "ha" 可以執行,而 x = x + "ha" 卻丟擲異常呢

迷渡發表於2013-03-11

問題

眾所周知,在 python 中,+ 運算子可以使用在列表上,+ 運算子只需要第二個運算元是可迭代的(原文:iterable。@justjavac),那麼 + 顯然可以運算在 "ha" 上。

程式碼如下:

>>> x = []
>>> x += "ha"
>>> x
['h', 'a']

>>> x = x + "ha"
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: can only concatenate list (not "str") to list

解答

當我們在列表 list 上使用 += 的時候,其實相當於呼叫函式 extend(),而不是使用的 +。

  • 你可以在一個可迭代(iterable)物件上呼叫 extend()。
    • 但是,當您使用 + 時,另一個運算元必須是列表(list)。

為什麼 python 會如此詭異,也許是出於效能方面的考慮。 呼叫 + 時,將會建立一個新的物件,並複製裡面的所有內容。但是當呼叫 extend() 函式時,將可以使用現有的空間。

這樣就會產生另一個副作用:如果你寫 X += Y,在其他對列表的引用(reference)中,會看到變化;但如果你使用 X = X + Y,就不會。

下面的程式碼說明了這一點:

>>> x = ['a','b']
>>> y = ['c', d']
>>> z = x
>>> x += y
>>> z
['a', 'b', 'c', 'd']    // z 也發生了變化

>>> x = ['a','b']
>>> y = ['c', d']
>>> z = x
>>> x = x + y
>>> z
['a', 'b']  // z 函式原始值

參考文獻

Python source code for list.

python:+= 的原始碼:

static PyObject *
list_inplace_concat(PyListObject *self, PyObject *other)
{
    PyObject *result;

    result = listextend(self, other);
    if (result == NULL)
        return result;
    Py_DECREF(result);
    Py_INCREF(self);
    return (PyObject *)self;
}

python:+ 的原始碼:

static PyObject *
list_concat(PyListObject *a, PyObject *bb)
{
    Py_ssize_t size;
    Py_ssize_t i;
    PyObject **src, **dest;
    PyListObject *np;
    if (!PyList_Check(bb)) {
        PyErr_Format(PyExc_TypeError,
                  "can only concatenate list (not \"%.200s\") to list",
                  bb->ob_type->tp_name);
        return NULL;
    }

    // etc ...

原文:python - If x is list, why does x += "ha" work, while x = x + "ha" throw an exception?

譯文:在 python 中,如果 x 是 list,為什麼 x += "ha" 可以執行,而 x = x + "ha" 卻丟擲異常呢?

譯者:justjavac

相關文章