By using this site, you agree to our updated Privacy Policy and our Terms of Use. Manage your Cookies Settings.
445,885 Members | 1,499 Online
Bytes IT Community
+ Ask a Question
Need help? Post your question and get tips & solutions from a community of 445,885 IT Pros & Developers. It's quick & easy.

decorator to prevent adding attributes to class?

P: n/a
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?

Jul 11 '08 #1
Share this Question
Share on Google+
5 Replies


P: n/a
On Jul 11, 5:29*pm, Neal Becker <ndbeck...@gmail.comwrote:
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/0...-con-i-mixin1/

There is also a recipe that does exactly what you want by means of a
metaclass:
http://aspn.activestate.com/ASPN/Coo.../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
Jul 11 '08 #2

P: n/a
Michele Simionato wrote:
This article could give you same idea (it is doing the opposite,
warning you
if an attribute is overridden):
http://stacktrace.it/articoli/2008/0...-con-i-mixin1/

There is also a recipe that does exactly what you want by means of a
metaclass:
http://aspn.activestate.com/ASPN/Coo.../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....2e859b9c002b28

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
Jul 11 '08 #3

P: n/a
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.
Jul 11 '08 #4

P: n/a
Robert Bossy wrote:
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.
Jul 11 '08 #5

P: n/a
On Jul 11, 9:24*pm, Neal Becker <ndbeck...@gmail.comwrote:
Robert Bossy wrote:
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.
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.
Jul 12 '08 #6

This discussion thread is closed

Replies have been disabled for this discussion.