Apply this decorator to a class with __slots__. Members will be mutable during the execution of __init__ but read-only afterwards.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | def immutable(mutableclass):
""" Decorator for making a slot-based class immutable """
if not isinstance(type(mutableclass), type):
raise TypeError('@immutable: must be applied to a new-style class')
if not hasattr(mutableclass, '__slots__'):
raise TypeError('@immutable: class must have __slots__')
class immutableclass(mutableclass):
__slots__ = () # No __dict__, please
def __new__(cls, *args, **kw):
new = mutableclass(*args, **kw) # __init__ gets called while still mutable
new.__class__ = immutableclass # locked for writing now
return new
def __init__(self, *args, **kw): # Prevent re-init after __new__
pass
# Copy class identity:
immutableclass.__name__ = mutableclass.__name__
immutableclass.__module__ = mutableclass.__module__
# Make read-only:
for name, member in mutableclass.__dict__.items():
if hasattr(member, '__set__'):
setattr(immutableclass, name, property(member.__get__))
return immutableclass
|
This decorator works by altering the __class__ after the instance is created to a subclass where all settable members are replaced with read-only descriptors. Of course, you can always set __class__ to the parent class again to make it read-write again.
Some ideas for improvements:
- Use a metaclass instead of a decorator
- Make it possible to inherit from an immutable class