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.