Last active
January 4, 2016 06:39
-
-
Save radix/8583267 to your computer and use it in GitHub Desktop.
Immutable metaclass
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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