Attributes PEP Enhancement

Joe Salmeri JoeSalmeri at home.com
Thu Aug 3 16:58:25 EDT 2000


I'm coming into this discussion a little late but I have been working on
something similiar to what you have been discussing.

I created the GetSetAttrib class as an attempt to force references to get or
set an attribute to go through a handler.
The handler can do whatever it wants to resolve the request.  Currently I am
just gettting/setting an attribute called '_????' in the instances
dictionary but that is not a requirement.

I have been using this module for a week or so and have yet to find a
situation where it caused a problem.

I would appreciate any comments or suggestions you might have regarding this
method of using attribute handlers.



#**************************************************************************
# GetSetAttrib Class
#**************************************************************************

class GetSetAttrib:

    def __getattr__(self, name):

        if debug: print 'Attempting to GET: %s' % (name)

        if (name[0:6] == '__get_') or (name[0:6] == '__set_'):
            raise AttributeError, 'Attribute "%s" not found' % (name)

        get_method_name = '__get_%s__' % (name)

        try:
            return getattr(self,get_method_name)()
        except AttributeError:
            raise AttributeError, 'Read of attribute "%s" failed - no %s
method found' % (name, get_method_name)

    def __setattr__(self, name, value):

        if debug: print 'Attempting to SET: %s' % (name)

        set_method_name = '__set_%s__' % (name)

        try:
            getattr(self,set_method_name)(value)
        except AttributeError:
            raise AttributeError, 'Write of attribute "%s" failed - no %s
method found' % (name, set_method_name)

#**************************************************************************
# Module Test
#**************************************************************************

def Test():

    class TestGetSetAttrib(GetSetAttrib):

        def __init__(self):
            self.__dict__['_x'] = 'value of x'
            self.__dict__['_y'] = 'value of y'

        def __get_x__(self):
            return self.__dict__['_x']

        def __set_x__(self, value):
            self.__dict__['_x'] = value

        def __get_y__(self):
            return self.__dict__['_y']

    print 'Running Test cases\n'

    i = TestGetSetAttrib()

    if i.x == 'value of x': print 'SUCCESS: GET the value of x'
    if i.y == 'value of y': print 'SUCCESS: GET the value of y'

    i.x = 'NEW value of x'

    if i.x == 'NEW value of x': print 'SUCCESS: SET the value of x'

    try:
        i.y = 'NEW value of y'
    except AttributeError, error_msg:
        if str(error_msg) == 'Write attribute "y" failed - no __set_y__
method found':
            print 'SUCCESS: EXCEPTION fired when SET of READ-ONLY y value
attempted'
        else:
            print 'FAILURE: %s' % error_msg

#**************************************************************************
# Module Initialization
#**************************************************************************

if (__name__ == '__main__'):    Test()




"Paul Prescod" <paul at prescod.net> wrote in message
news:397BDB62.9BA93128 at prescod.net...
> Okay, the strong preference of people on the list is for separate method
> handlers for __set_XXX__ and __get_XXX___. I propose the following
> algorithm to try and keep performance reasonable (though probably not as
> good as with the single-method proposal).
>
> When Python sees a class like this:
>
> class Foo:
>     def __set_XXX__( self ):
>         pass
>
> It would add two things to the class's dict.
>
> __set_XXX__ = <unbound method>
> XXX = <attribute handler flag object>
>
> Now let's say you see this code:
>
> Foo().XXX
>
> This will find the attribute handler object. It will discard that object
> immediately and *restart* its search at the beginning, now looking for
> __get_XXX__.  In other words, it detects that XXX is a "special
> attribute" because of an entry in the dictionary but it uses full Python
> inheritance rules to find the appropriate method. In essence the
> attribute handler object is *just a flag* not a useful piece of
> information. The flag is important because how else would we know when
> to look for a __get_XXX__ function? When we are looking up attributes
> that do NOT have handlers, the performance should be identical to today.
>
> If you do this:
>
> foo=Foo()
> foo.__dict__["XXX"]=5
>
> ...you will blow away all three attribute handlers for that attribute.
> You can get them back by reassigning the attribute handler flag object
> to foo.XXX.
>
> If there is no getattr defined then it looks for a generic __getattr__
> (as per usual) and otherwise raises AttributeError.
>
> Setattr works like this: First it does a getattr to see if there is an
> attribute handler object. This first getattr can be optimized away for
> objects that have no attribute handlers at all. If there is no attribute
> handler flag for XXX, the code continues as with today's Python.
>
> If there is a handler flag, Python looks for a setter function. If it
> finds one, it calls it. If it can't find it then we have somewhat of a
> problem. We can either just allow the write using traditional Python
> rules or raise an AttributeError. I think that the latter is appropriate
> because remember what the former will do. It will *overwrite your
> handler*. That isn't a good default action. Another possible default
> action is to write to __XXX instead of XXX.
>
> It would be nice if foo.__dict__["XXX"] were "available" for use rather
> than reserved by the mechanism but I don't see how to work that and
> still get decent performance for property gets -- especially property
> gets that are NOT related to the feature. Anyhow, hacking your
> dictionary is ugly. Just use foo.__xxx .
>
> --
>  Paul Prescod - Not encumbered by corporate consensus
> New from Computer Associates: "Software that can 'think', sold by
> marketers who choose not to."
>





More information about the Python-list mailing list