There are a few things happening here.
+=
is not always +
and then =
.
+=
and +
can have different implementations if required.
Take a look at this example.
In [13]: class Foo:
...: def __init__(self, x=0):
...: self.x = x
...: def __add__(self, other):
...: print('+ operator used')
...: return Foo(self.x + other.x)
...: def __iadd__(self, other):
...: print('+= operator used')
...: self.x += other.x
...: return self
...: def __repr__(self):
...: return f'Foo(x={self.x})'
...:
In [14]: f1 = Foo(10)
In [15]: f2 = Foo(20)
In [16]: f3 = f1 + f2
+ operator used
In [17]: f3
Out[17]: Foo(x=30)
In [18]: f1
Out[18]: Foo(x=10)
In [19]: f2
Out[19]: Foo(x=20)
In [20]: f1 += f2
+= operator used
In [21]: f1
Out[21]: Foo(x=30)
Similarly, the list class has separate implementations for +
and +=
.
Using +=
actually does an extend
operation in the background.
In [24]: l = [1, 2, 3, 4]
In [25]: l
Out[25]: [1, 2, 3, 4]
In [26]: id(l)
Out[26]: 140009508733504
In [27]: l += [5, 6, 7]
In [28]: l
Out[28]: [1, 2, 3, 4, 5, 6, 7]
In [29]: id(l)
Out[29]: 140009508733504
Using +
creates a new list.
In [31]: l
Out[31]: [1, 2, 3]
In [32]: id(l)
Out[32]: 140009508718080
In [33]: l = l + [4, 5, 6]
In [34]: l
Out[34]: [1, 2, 3, 4, 5, 6]
In [35]: id(l)
Out[35]: 140009506500096
Let's come to your question now.
In [36]: t = ([1, 2], [3, 4])
In [37]: t[0] += [10, 20]
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
<ipython-input-37-5d9a81f4e947> in <module>
----> 1 t[0] += [10, 20]
TypeError: 'tuple' object does not support item assignment
In [38]: t
Out[38]: ([1, 2, 10, 20], [3, 4])
The +
operator gets executed first here, which means the list gets updated (extended). This is allowed as the reference to the list (value stored in the tuple) doesn't change, so this is fine.
The =
then tries to update the reference inside the tuple
which isn't allowed since tuples are immutable.
But the actual list was mutated by the +
.
Python fails to update the reference to the list inside the tuple but since it would have been updated to the same reference, we, as users don't see the change.
So, the +
gets executed and the =
fails to execute. +
mutates the already referenced list
inside the tuple
so we see the mutation in the list.
+=
? Is there some kind of trade-off for in-place ops wrt list? – Stealingx[0].extend([3,4])
does work as expected however... – Rebatementx += y
is always welcome. When slicing / index / attribute is used, considering mutability is needed. – Okajima