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