I suggest using __setattr__ to avoid the oddities of __slots__ .
You should always be careful when starting with __setattr__ , as it takes care of setting all the attributes of the instance, including the ones you set in __init__ . Therefore, it should have some way of knowing when to allow the setting of an attribute and when to deny it. In this solution, I assigned a special attribute that controls whether new attributes are allowed or not:
class A(object): def __init__(self): self.a = 1 self.b = 2 self.c = 3 self.freeze = True def __setattr__(self, attr, value): if getattr(self, "freeze", False) and not hasattr(self, attr): raise AttributeError("You shall not set attributes!") super(A, self).__setattr__(attr, value)
Testing:
a = A() try: ad = 89 except AttributeError: print "It works!" else: print "It doesn't work." ac = 42 print aa print ac a.freeze = False ad = 28 a.freeze = True print ad
Result:
It works!
1
42
28
Also see the gnibblers answer , which neatly completes this concept in the class decorator, so it does not clutter the class definition and can be reused in multiple classes without duplicate code.
EDIT:
Returning to this answer after a year, I understand that the context manager can solve this problem even better. Here's a modified version of the gnibbler class decorator:
from contextlib import contextmanager @contextmanager def declare_attributes(self): self._allow_declarations = True try: yield finally: self._allow_declarations = False def restrict_attributes(cls): cls.declare_attributes = declare_attributes def _setattr(self, attr, value): disallow_declarations = not getattr(self, "_allow_declarations", False) if disallow_declarations and attr != "_allow_declarations": if not hasattr(self, attr): raise AttributeError("You shall not set attributes!") super(cls, self).__setattr__(attr, value) cls.__setattr__ = _setattr return cls
And here is how to use it:
@restrict_attributes class A(object): def __init__(self): with self.declare_attributes(): self.a = 1 self.b = 2 self.c = 3
Therefore, when you want to set new attributes, just use the with statement as described above. This can also be done from outside the instance:
a = A() try: ad = 89 except AttributeError: print "It works!" else: print "It doesn't work." ac = 42 print aa print ac with a.declare_attributes(): ad = 28 print ad