Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Ways to make a class immutable in Python

I'm doing some distributed computing in which several machines communicate under the assumption that they all have identical versions of various classes. Thus, it seems to be good design to make these classes immutable; not in the sense that it must thwart a user with bad intentions, just immutable enough that it is never modified by accident.

How would I go about this? For example, how would I implement a metaclass that makes the class using it immutable after it's definition?

>>> class A(object):
...     __metaclass__ = ImmutableMetaclass
>>> A.something = SomethingElse # Don't want this
>>> a = A()
>>> a.something = Whatever # obviously, this is still perfectly fine.

Alternate methods is also fine, such as a decorator/function that takes a class and returns an immutable class.

like image 557
porgarmingduod Avatar asked Feb 14 '11 20:02

porgarmingduod


People also ask

How do you define an immutable object in Python?

Immutable Objects : These are of in-built types like int, float, bool, string, unicode, tuple. In simple words, an immutable object can't be changed after it is created. Mutable Objects : These are of type list, dict, set . Custom classes are generally mutable.

What Python types are immutable?

Objects of built-in type that are immutable are: Numbers (Integer, Rational, Float, Decimal, Complex & Booleans) Strings. Tuples.


2 Answers

If the old trick of using __slots__ does not fit you, this, or some variant of thereof can do: simply write the __setattr__ method of your metaclass to be your guard. In this example, I prevent new attributes of being assigned, but allow modification of existing ones:

def immutable_meta(name, bases, dct):
    class Meta(type):
        def __init__(cls, name, bases, dct):
            type.__setattr__(cls,"attr",set(dct.keys()))
            type.__init__(cls, name, bases, dct)

        def __setattr__(cls, attr, value):
            if attr not in cls.attr:
                raise AttributeError ("Cannot assign attributes to this class")
            return type.__setattr__(cls, attr, value)
    return Meta(name, bases, dct)


class A:
    __metaclass__ = immutable_meta
    b = "test"

a = A()
a.c = 10 # this works
A.c = 20 # raises valueError
like image 144
jsbueno Avatar answered Nov 06 '22 01:11

jsbueno


Don't waste time on immutable classes.

There are things you can do that are far, far simpler than messing around with trying to create an immutable object.

Here are five separate techniques. You can pick and choose from among them. Any one will work. Some combinations will work, also.

  1. Documentation. Actually, they won't forget this. Give them credit.

  2. Unit test. Mock your application objects with a simple mock that handles __setattr__ as an exception. Any change to the state of the object is a fail in the unit test. It's easy and doesn't require any elaborate programming.

  3. Override __setattr__ to raise an exception on every attempted write.

  4. collections.namedtuple. They're immutable out of the box.

  5. collections.Mapping. It's immutable, but you do need to implement a few methods to make it work.

like image 31
S.Lott Avatar answered Nov 06 '22 01:11

S.Lott