Search code examples
pythontuplesimmutabilitypep

Assignment to a mutable tuple component in python: a bug? a feature?


We know that Python tuples are immutable, good. When I attempt to change the reference of a tuple component I get an exception, as expected. What is not expected, is the component gets changed regardless of the exception, whereas I would have thought tuple immutability guarantees that the object won't be mutable.

Is it a bug, feature or a PEP?

In [6]: x=([1],)
In [7]: type(x)
Out[7]: tuple
In [8]: x[0]+=[2,3]
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-8-a73186f99454> in <module>()
----> 1 x[0]+=[2,3]

TypeError: 'tuple' object does not support item assignment   
In [9]: x
Out[9]: ([1, 2, 3],)

Solution

  • Interesting point.

    The reason it behaves like this is that

    x[0]+=[2,3]
    

    translates to

    x[0] = x[0].__iadd__([2,3])
    

    which means it first calls __iadd__, which modifies the list in place, and only then attempts to perform the illegal assignment into the tuple.

    (Of course, it's easy to workaround (e.g. @luispedro's answer), but I understand your question is not about how to workaround it.)

    Is it a bug, feature or a PEP?

    Hard to say. I tend to vote for "a bug", because of the Principle of Least Astonishment. One would expect x[0].extend(y) to behave like a=x[0]; a.extend(y) to behave like a=x[0]; a+=y to behave like x[0]+=y.

    A possible fix (at least for python built-in types) can be to required that __setitem__(self, k, v) should translate to no-op in case self[k] is v. (and custom classes overriding __setitem__ should obey).