Ways to make a class immutable in Python

2019-02-08 05:49发布

问题:

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.

回答1:

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


回答2:

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.



回答3:

If you don't mind reusing someone else's work:

http://packages.python.org/pysistence/

Immutable persistent (in the functional, not write to desk sense) data structures.

Even if you don't use them as is, the source code should provide some inspiration. Their expando class, for example, takes an object in it's constructor and returns an immutable version of it.