List extending strange behaviour [duplicate]

Found interesting thing in Python (2.7) that never mentioned before.

This:

a = []
a += "a"

does work and result is:

>>> a
>>> ["a"]

But

a = []
a = a + "a"

gives

>>> TypeError: can only concatenate list (not "str") to list

Can someone explain why? Thanks for your answers.


Solution 1:

Python distinguishes between the + and += operators and provides separate hooks for these; __add__ and __iadd__. The list() type simply provides a different implementation for the latter.

It is more efficient for lists to implement these separately; __add__ has to return a completely new list, while __iadd__ can just extend self then return self.

In the C code, __iadd__ is implemented by list_inplace_concat(), which simply calls listextend(), or, in python code, [].extend(). The latter takes any sequence, by design.

The __add__ method on the other hand, represented in C by list_concat, only takes a list as input, probably for efficiency's sake; it can loop directly over the internal C array and copy items over to the new list.

In conclusion, the reason __iadd__ accepts any sequence is because when PEP 203 (the Augmented Add proposal) was implemented, for lists it was simplest just to reuse the .extend() method.

Solution 2:

If a is a list, a + x only works if x is also list, whereas a += x works for any iterable x.

The following might help understand it:

In [4]: a = []

In [5]: a += "abc"

In [6]: a
Out[6]: ['a', 'b', 'c']

The key is that "a" and "abc" are iterable, which is what enables their use on the right-hand side of +=.

This doesn't work for + because the latter requires both operands to be of the same type (see the manual).

To write the same thing using +, you have to expand the iterable:

In [7]: a = []

In [8]: a = a + list("abc")

In [9]: a
Out[9]: ['a', 'b', 'c']

In other words, += is more general than + when applied to lists.