Skip to content

Instantly share code, notes, and snippets.

@radix
Last active January 4, 2016 06:39
Show Gist options
  • Save radix/8583267 to your computer and use it in GitHub Desktop.
Save radix/8583267 to your computer and use it in GitHub Desktop.
Immutable metaclass
from collections import namedtuple
def immutable(fields):
"""
Returns a metaclass that makes fields on your instances immutable, while
still allowing __init__ to initialize attributes.
You should be able to enable immutability on most normal classes by just
adding
__metaclass__ = immutable(['attribute_a', 'attribute_b'])
"""
class Immutable(type):
def __new__(meta, name, bases, dct):
tuptype = namedtuple(name + "_tuple", fields)
class Foo(tuptype):
def __new__(klass):
helper = _ConstructionHelper()
init_func = klass.__init__.im_func
init_func(helper)
initial_data = []
for field in fields:
initial_data.append(getattr(helper, field))
del klass.__init__
return super(Foo, klass).__new__(klass, *initial_data)
return super(Immutable, meta).__new__(meta, name, (Foo,) + bases, dct)
return klass
return Immutable
class _ConstructionHelper(object):
"""
Instances of this are passed to __init__ in immutable classes.
"""
if __name__ == '__main__':
class MyClass(object):
__metaclass__ = immutable(["a", "b"])
def __init__(self):
self.a = 1
self.b = 2
my = MyClass()
assert my.a == 1
assert my.b == 2
try:
my.a = 2
except AttributeError:
print "good: attribute-error on set"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment