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 ---