decorator to prevent adding attributes to class?

N

Neal Becker

After spending the morning debugging where I had misspelled the name of an
attribute (thus adding a new attr instead of updating an existing one), I
would like a way to decorate a class so that attributes cannot be (easily)
added.

I guess class decorators are not available yet (pep 3129), but probably
inheritance can be used.

Can anyone suggest an implementation?
 
M

Michele Simionato

After spending the morning debugging where I had misspelled the name of an
attribute (thus adding a new attr instead of updating an existing one), I
would like a way to decorate a class so that attributes cannot be (easily)
added.

I guess class decorators are not available yet (pep 3129), but probably
inheritance can be used.

Can anyone suggest an implementation?

This article could give you same idea (it is doing the opposite,
warning you
if an attribute is overridden):
http://stacktrace.it/articoli/2008/06/i-pericoli-della-programmazione-con-i-mixin1/

There is also a recipe that does exactly what you want by means of a
metaclass:
http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/252158
It is so short I can write it down here:
# requires Python 2.2+

def frozen(set):
"Raise an error when trying to set an undeclared name."
def set_attr(self,name,value):
if hasattr(self,name):
set(self,name,value)
else:
raise AttributeError("You cannot add attributes to %s" %
self)
return set_attr

class Frozen(object):
"""Subclasses of Frozen are frozen, i.e. it is impossibile to add
new attributes to them and their instances."""
__setattr__=frozen(object.__setattr__)
class __metaclass__(type):
__setattr__=frozen(type.__setattr__)

Of course using frozen classes is not Pythonic at all, and I wrote the
recipe as
a proof of concept, not to use it.

Michele Simionato
 
R

Robert Bossy

Michele said:
This article could give you same idea (it is doing the opposite,
warning you
if an attribute is overridden):
http://stacktrace.it/articoli/2008/06/i-pericoli-della-programmazione-con-i-mixin1/

There is also a recipe that does exactly what you want by means of a
metaclass:
http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/252158
It is so short I can write it down here:
# requires Python 2.2+

def frozen(set):
"Raise an error when trying to set an undeclared name."
def set_attr(self,name,value):
if hasattr(self,name):
set(self,name,value)
else:
raise AttributeError("You cannot add attributes to %s" %
self)
return set_attr

class Frozen(object):
"""Subclasses of Frozen are frozen, i.e. it is impossibile to add
new attributes to them and their instances."""
__setattr__=frozen(object.__setattr__)
class __metaclass__(type):
__setattr__=frozen(type.__setattr__)
I don't get it. Why use a metaclass? Wouldn't the following be the same,
but easier to grasp:

class Frozen(object):
def __setattr__(self, name, value):
if not hasattr(self, name):
raise AttributeError, "cannot add attributes to %s" % self
object.__setattr__(self, name, value)

Btw, the main drawback with Frozen is that it will not allow to set any
new attributes even inside __init__.


Some people would advise to use __slots__:
http://docs.python.org/ref/slots.html#l2h-222
Some other people would advise NOT to use __slots__:
http://groups.google.com/group/comp.lang.python/msg/0f2e859b9c002b28



Personally, if I must absolutely, I'd go for explicitely freeze the
object at the end of __init__:

class Freezeable(object):
def freeze(self):
self._frozen = None

def __setattr__(self, name, value):
if hasattr(self, '_frozen') and not hasattr(self, name):
raise AttributeError
object.__setattr__(self, name, value)


class Foo(Freezeable):
def __init__(self):
self.bar = 42
self.freeze() # ok, we set all variables, no more from here


x = Foo()
print x.bar
x.bar = -42
print x.bar
x.baz = "OMG! A typo!"


Cheers,
RB
 
M

Michele Simionato

On Jul 11, 6:38 pm, Robert Bossy
I don't get it. Why use a metaclass? Wouldn't the following be the same,
but easier to grasp:

class Frozen(object):
    def __setattr__(self, name, value):
       if not hasattr(self, name):
          raise AttributeError, "cannot add attributes to %s" % self
       object.__setattr__(self, name, value)

This is easier, but it does not stop the user from
adding class level attributes: this is the job
of the metaclass. If you don't care about
class level attributes (including methods,
properties, etc) and you are content with restricting
only the instance attributes your recipe is fine, yes.
 
N

Neal Becker

Robert said:
class Foo(Freezeable):
def __init__(self):
self.bar = 42
self.freeze() # ok, we set all variables, no more from here


x = Foo()
print x.bar
x.bar = -42
print x.bar
x.baz = "OMG! A typo!"

Pretty nice, but unfortunately the subclass has to remember to call freeze
in it's init. Too bad that can't be automated.
 
M

Michele Simionato

Pretty nice, but unfortunately the subclass has to remember to call freeze
in it's init.  Too bad that can't be automated.

It can with a metaclass redefining the __call__ method
to call freeze after instantation. But there
would a lot of magic going on such a design.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,774
Messages
2,569,599
Members
45,165
Latest member
JavierBrak
Top