Adding some context on top of veedrac's answer.
When using augmented assignment operators with sequences (+=
or *=
), the special/dunder methods __iadd__
(in-place addition) or __imult__
(in-place multiplication) will be called respectively for +=
or *=
. Those methods are implemented for list
but not for tuple
. If those methods are not implemented Python will fall back on the __add__
or __mult__
which both return a new object.
Those are the dunder methods being called when directly calling the +
or *
operator on list or tuple. (l3 = l1 + l2
where l1
and l2
are lists or t2 = t1 * 2
for t2
being a tuple)
This explains the difference of behavior between:
- augmented assignment operators on tuple
>>> tup = (1, 2, 3)
>>> id(tup)
140153476307856
>>> tup += (4, 5)
>>> id(tup)
140153479825840
- augmented assignment operators on list
>>> lst = [1, 2, 3]
>>> id(lst)
140153476247704
>>> lst += [4, 5]
>>> id(lst)
140153476247704
Please note that using those operations on tuple in a loop is inefficient because the interpreter has to copy whole target object first before doing the concatenation and returning a new object, which isn't the case when the operation is done in-place.
import time
start_time = time.time()
l1 = [1, 2, 3]
l2 = [4, 5]
for _ in range(100000):
l1 += l2
print("--- list: %s seconds ---" % (time.time() - start_time))
start_time = time.time()
t1 = (1, 2, 3)
t2 = (4, 5)
for _ in range(100000):
t1 += t2
print("--- tuple: %s seconds ---" % (time.time() - start_time))
gives as output:
--- list: 0.0055124759674072266 seconds ---
--- tuple: 20.920572996139526 seconds ---