Python – How to make an immutable object in Python

immutabilitypythonpython-3.x

Although I have never needed this, it just struck me that making an immutable object in Python could be slightly tricky. You can't just override __setattr__, because then you can't even set attributes in the __init__. Subclassing a tuple is a trick that works:

class Immutable(tuple):
    
    def __new__(cls, a, b):
        return tuple.__new__(cls, (a, b))

    @property
    def a(self):
        return self[0]
        
    @property
    def b(self):
        return self[1]

    def __str__(self):
        return "<Immutable {0}, {1}>".format(self.a, self.b)
    
    def __setattr__(self, *ignored):
        raise NotImplementedError

    def __delattr__(self, *ignored):
        raise NotImplementedError

But then you have access to the a and b variables through self[0] and self[1], which is annoying.

Is this possible in Pure Python? If not, how would I do it with a C extension?

(Answers that work only in Python 3 are acceptable).

Update:

As of Python 3.7, the way to go is to use the @dataclass decorator, see the newly accepted answer.

Best Answer

Yet another solution I just thought of: The simplest way to get the same behaviour as your original code is

Immutable = collections.namedtuple("Immutable", ["a", "b"])

It does not solve the problem that attributes can be accessed via [0] etc., but at least it's considerably shorter and provides the additional advantage of being compatible with pickle and copy.

namedtuple creates a type similar to what I described in this answer, i.e. derived from tuple and using __slots__. It is available in Python 2.6 or above.