This is because with extend
and the dict you're mutating the objects themselves after retrieving them, rather than trying to assign to the tuple.
t[2] += [5, 6]
is actually kind of equivalent to
t[2] = t[2] + [5, 6]
which is clearly not allowed. (This calls __setitem__
under the hood)
However, t[2].extend([5, 6])
only gets the item (using __getitem__
, which is permitted), and once it has the object is uses extend
. The object is still the same object though - you may think of it as the pointer not having changed.
The case in your comment is an interesting one - again, it gets the object in the first statement b = t[2]
and then already having the object mutates the object itself. You can see that this won't result in any call to the tuple's __setitem__
. Refer to the dupe link for info on why it's possible to mutate tuple elements.