views:

1658

answers:

3

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

Thanks in advance.

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
+25  A: 

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 ...
Mark Byers
I think the real question here is, "why such inconsistency?"
doublep
i'm on the verge of going -1 on this answer as it doesn't answer the question at all (see @doublep's comment).
just somebody
I don't think it's clear at all that this question is a critique of the design. The first step has to be to understand how the inconsistency is implemented, and this is all we can help with here. The larger questions that you commenters ask are completely outside of the scope of SO, if you ask me :)
Magnus Hoff
Wow, what a great answer. Meanwhile, I did a search through the PDF I'm reading and it actually says + is concat while += is extend(). The only problem is it says that ~300 pages after the introduction of the + op! And I must admit it was a real shock, seeing as everything else was very intuitive (till now anyway). Thanks!
masterridley
@doublep: Practicality beats purity.
ΤΖΩΤΖΙΟΥ
+6  A: 

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.

Chris B.
another tentative -1: to me it's obvious that `x += y` is defined as `x = x + y` for any `x` and `y`. it's immediately clear that you have avoided answering the question. ;)
just somebody
I think the point here is that it is *not* obvious, hence the question. In most other programming languages where both `+=` and `+` is defined, doing `x += y` is usually defined to be the exact same as `x = x + y`. In fact, typically one is an alias for the other.
Lasse V. Karlsen
It's obvious in the sense that, if you try it, it's very clear what happens. And it's not as if, if you weren't expecting it to work, you'll be disappointed when it does.
Chris B.
It is IMHO clear that generally if x=x+y would yield a desired effect, x+=y should also. The converse, however, is not true. An object might be constructed so that if two threads simultaneously execute "x+=y;" and "x+=z;" the result will be the same as if the statements executed in some order, but if they execute "x=x+y;" and "x=x+z;" there's no practical way to offer such a guarantee.
supercat
the previous comment ("tentative -1") from me addresses rev.1. rev.2 is interesting and wouldn't deserve -1 at all, also @supercat's comment is insightful.
just somebody
"just somebody" is making me seriously wish for the ability to downvote comments.
Glenn Maynard
+2  A: 

When defining operators, there are two different "add" operators: One is called __add__, the other __iadd__. The latter one is for in-place additions with +=, the other one is the regular + operator. http://docs.python.org/reference/datamodel.html has more infos on that.

kkaefer