design question: no new attributes
Arnaud Delobelle
arnodel at googlemail.com
Sun Mar 4 12:12:33 EST 2007
On Feb 26, 9:48 pm, "Alan Isaac" <ais... at american.edu> wrote:
> I have a class whose instances should only receive attribute
> assignments for attributes that were created at inititialization.
> If slots are not appropriate, what is the Pythonic design for this?
Hi !
Even though a lot of people have argued against such a thing, I have
been thinking about this last night and I have the following hack.
Classes descending from SuspiciousObject below won't allow new
attributes to be added to their instances apart from within trusted
methods (i.e. methods decorated with @trustedmethod)
Note: this is probably not of great interest but I've decided to share
it since I did this as a result of reading this thread :)
class LockableDict(dict):
"A dict where addition of new keys can be prevented by setting the
locked attribute"
__slots__ = ('locked',)
def __init__(self, locked=False):
self.locked = locked
def force_setitem(self, key, value):
super(LockableDict, self).__setitem__(key, value)
def __setitem__(self, key, value):
if self.has_key(key) or not self.locked:
self.force_setitem(key, value)
else:
raise KeyError, key
def trustedmethod(f):
def pf(self, *args, **kwargs):
was_locked = self.__dict__.locked
self.__dict__.locked = False
try:
return f(self, *args, **kwargs)
finally:
self.__dict__.locked = was_locked
return pf
class SuspiciousObject(object):
def __new__(cls, *args, **kwargs):
self = object.__new__(cls)
super(SuspiciousObject, self).__setattr__('__dict__',
LockableDict(locked=True))
return self
def __setattr__(self, attr, value):
try:
self.__dict__[attr] = value
except KeyError:
raise AttributeError, "'%s' object has no attribute '%s" %
(type(self).__name__, attr)
# Instances of SuspiciousObject refuse anyone the right to create a
new attribute apart from methods marked with the decorator
@trustedmethod.
# Example:
class Foo(SuspiciousObject):
@trustedmethod
def __init__(self):
self.bar = 2
self.baz = 'Hello'
def foobar(self, v):
self.fubar = v
@trustedmethod
def force_setattr(self, attr, val=None):
setattr(self, attr, val)
This would give something like:
>>> foo=Foo()
>>> foo.bar
2
>>> foo.baz
'Hello'
>>> foo.baz="Bye" # works as foo.baz exists
>>> foo.baz
'Bye'
>>> foo.fubar=4 # won't be trusted
...
AttributeError: 'Foo' object has no attribute 'fubar
>>> setattr(foo, 'fubar', 4) # won't work either
...
AttributeError: 'Foo' object has no attribute 'fubar
>>> foo.__dict__['fubar']=4 # Neither will this
...
KeyError: 'fubar'
>>> foo.foobar(4) # Neither will this as foo.foobar is not a trusted method
...
AttributeError: 'Foo' object has no attribute 'fubar
>>> foo.force_setattr('fubar', 4) # this will work as force_setattr is trusted
>>> foo.fubar
4
>>> # etc...
By creating a custom metaclass for SuspiciousObject, it would be easy
to make SuspiciousObjects trust their own methods by default instead
of having to declare which method are trusted.
--
Arnaud
More information about the Python-list
mailing list