Question

If x is list, why does x += "ha" work, while x = x + "ha" throws an exception?

From what little I know, + op for lists only requires the 2nd operand to be iterable, which "ha" clearly is.

In code:

>>> 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
 45  2886  45
1 Jan 1970

Solution

 31

Using += with a list is like calling extend, not +.

  • You can call extend with an iterable.
  • You can only use + with another list.

I can only guess why this decision was made, but I imagine it is for performance reasons. Calling + results in a new object being created and all items being copied, whereas extend can use free space in the existing list object saving a copy in some cases.

Another side-effect of this decision is that if you write x += y other references to the list will see the change but if you use x = x + y then they will not. This is demonstrated below:

>>> x = ['a','b']
>>> y = ['c', d']
>>> z = x
>>> x += y
>>> z
['a', 'b', 'c', 'd']

>>> x = ['a','b']
>>> y = ['c', d']
>>> z = x
>>> x = x + y
>>> z
['a', 'b']

References

Python source code for list.

Source code for +=:

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;
}

Source code for +:

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 ...
2010-07-09

Solution

 7

You're thinking about it backwards. You're asking why x = x + 'ha' throws an exception, given that x += 'ha' works. Really, the question is why x += 'ha' works at all.

Everyone agrees (I hope) that 'abc' + 'ha' and [1, 2, 3] + ['h', 'a'] should work. And in these cases, overloading += to do in-place modification seems reasonable.

The language designers decided that [1, 2, 3] + 'ha' shouldn't, because you're mixing different types. And that seems reasonable as well.

So the question is why they decided to allow mixing different types in the case of x += 'ha'. In this case, I imagine there are a couple reasons:

  • It's a convenient shorthand
  • It's obvious what happens (you append each of the items in the iterable to x)

In general, Python tries to let you do what you want, but where there's ambiguity, it tends to force you to be explicit.

2010-07-09