Immutability does not get conferred on mutable objects inside the tuple. All immutability means is you can't change which particular objects are stored - ie, you can't reassign items[0].set
. This restriction is the same regardless of the type of that variable - if it was a list, doing items[0].list = items[0].list + [1,2,3]
would fail (can't reassign it to a new object), but doing items[0].list.extend([1,2,3])
would work.
Think about it this way: if you change your code to: new_item = N(i,R,8)
then new_item.set
is now an alias for R (Python doesn't copy objects when you reassign them). If tuples conferred immutability to mutable members, what would you expect R.remove(1)
to do? Since it is the same set as new_item.set
, any changes you make to one will be visible in the other. If the set had become immutable because it has become a member of a tuple, R.remove(1)
would suddenly fail. All method calls in Python work or fail depending on the object only, not on the variable - R.remove(1)
and new_item.set.remove(1)
have to behave the same way.
This also means that:
R = set(range(0,8))
for i in range(0,8):
items.append(N(i,R,8))
probably has a subtle bug. R
never gets reassigned here, and so every namedtuple in items
gets the same set. You can confirm this by noticing that items[0].set is items[1].set
is True. So, anytime you mutate any of them - or R - the modification would show up everywhere (they're all just different names for the same object).
This is a problem that usually comes up when you do something like
a = [[]] * 3
a[0].append(2)
and a will now be [[2], [2], [2]]
. There are two ways around this general problem:
First, be very careful to create a new mutable object when you assign it, unless you do deliberately want an alias. In the nested lists example, the usual solution is to do a = [[] for _ in range(3)]
. For your sets in tuples, move the line R = ...
to inside the loop, so it gets reassigned to a new set for each namedtuple
.
The second way around this is to use immutable types. Make R
a frozenset
, and the ability to add and remove elements goes away.