I’m a bit confused about modifying tuple members. The following doesn’t work:
>>> thing = (['a'],)
>>> thing[0] = ['b']
TypeError: 'tuple' object does not support item assignment
>>> thing
(['a'],)
But this does work:
>>> thing[0][0] = 'b'
>>> thing
(['b'],)
Also works:
>>> thing[0].append('c')
>>> thing
(['b', 'c'],)
Doesn’t work, and works (huh?!):
>>> thing[0] += 'd'
TypeError: 'tuple' object does not support item assignment
>>> thing
(['b', 'c', 'd'],)
Seemingly equivalent to previous, but works:
>>> e = thing[0]
>>> e += 'e'
>>> thing
(['b', 'c', 'd', 'e'],)
So what exactly are the rules of the game, when you can and can’t modify something inside a tuple? It seems to be more like prohibition of using the assignment operator for tuple members, but the last two cases are confusing me.
You can always modify a mutable value inside a tuple. The puzzling behavior you see with
is caused by
+=. The+=operator does in-place addition but also an assignment — the in-place addition works just file, but the assignment fails since the tuple is immutable. Thinking of it likeexplains this better. We can use the
dismodule from the standard library to look at the bytecode generated from both expressions. With+=we get anINPLACE_ADDbytecode:With
+we get aBINARY_ADD:Notice that we get a
STORE_FASTin both places. This is the bytecode that fails when you try to store back into a tuple — theINPLACE_ADDthat comes just before works fine.This explains why the “Doesn’t work, and works” case leaves the modified list behind: the tuple already has a reference to the list:
The list is then modified by the
INPLACE_ADDand theSTORE_FASTfails:So the tuple still has a reference to the same list, but the list has been modified in-place: