tags:

views:

55

answers:

3

I want to be able to create a class (in Python) that once initialized with __init__, does not accept new attributes, but accepts modifications of existing attributes. There's several hack-ish ways I can see to do this, for example having a __setattr__ method such as

def __setattr__(self, attribute, value):
    if not attribute in self.__dict__:
        print "Cannot set %s" % attribute
    else:
        self.__dict__[attribute] = value

and then editing __dict__ directly inside __init__, but I was wondering if there is a 'proper' way to do this?

+3  A: 

The proper way is to override __setattr__. That's what it's there for.

katrielalex
What is then the proper way to set variables in `__init__` ? Is it to set them in `__dict__` directly?
astrofrog
I would override `__setattr__` in `__init__`, by `self.__setattr__ = <new-function-that-you-just-defined>`.
katrielalex
+3  A: 

Actually, you don't want __setattr__, you want __slots__. Add __slots__ = ('foo', 'bar', 'baz') to the class body, and Python will make sure that there's only foo, bar and baz on any instance. But read the caveats the documentation lists!

delnan
Using `__slots__` works, but it will break serialization (e.g. pickle), among other things... It's usually a bad idea to use slots to control attribute creation, rather than reduce memory overhead, in my opinion, anyway...
Joe Kington
I know, and I hestiate to use it myself - but doing extra work to disallow new attributes is usually a bad idea, too ;)
delnan
@delnan - True!
Joe Kington
+4  A: 

I wouldn't use __dict__ directly, but you can add a function to explicitly "freeze" a instance:

class FrozenClass(object):
    __isfrozen = False
    def __setattr__(self, key, value):
        if self.__isfrozen and not hasattr(self, key):
            raise TypeError( "%r is a frozen class" % self )
        object.__setattr__(self, key, value)

    def _freeze(self):
        self.__isfrozen = True

class Test(FrozenClass):
    def __init__(self):
        self.x = 42#
        self.y = 2**3

        self._freeze() # no new attributes after this point.

a,b = Test(), Test()
a.x = 10
b.z = 10 # fails
THC4k