Order in metaclass

Carlos Ribeiro carribeiro at gmail.com
Thu Oct 14 09:08:08 EDT 2004


On Thu, 14 Oct 2004 09:42:24 +0200, Alex Martelli <aleaxit at yahoo.com> wrote:
> Carlos Ribeiro <carribeiro at gmail.com> wrote:
>    ...
> > The code is as follows -- still with debug code included, and lots of
> comments:
> 
> Nice!  I must be missing something (what's the role of the '__attr__'
> key you're setdefaulting into instance dicts and never using
> otherwise?), and there may be enhanceable spots such as:

The relevant part of the code is as follows (debug code removed):

    def __get__(self, instance, owner):
        if instance:
            attrdict = instance.__dict__.setdefault('__attr__', {})
            return attrdict.get(self.name, self.value)
        else:
            return self
    def __set__(self, instance, value):
        attrdict = instance.__dict__.setdefault('__attr__', {})
        attrdict[self.name] = value

The __attr__ is used as part of my descriptor implementation, and it's
an attribute of the instance. Its a dictionary whose keys are the
names of the GenericAttributes (or TypedAttributes) stored in the
instance.

The __get__() code checks if its being called from a class; in this
case, it returns the attribute itself (so I can check its name, and
other properties of the attribute, when working with class
references). But if the attribute is being called from an instance,
then it retrieves the current value of the attribute, as stored in the
instance; if the attribute wasn't initialized, it retrieves the
default value (that is an attribute of the descriptor).

An example may help to clarify things a little bit:

class Person(GenericTemplate)
    name = TypedAttribute('unknown')

john = Person()
john.name = "John Doe"
peter = Person()

In this case, john.__attr__['name'] stores the current value of the
name attribute at the instance. john.name will retrieve this value.
peter.__attr__ wasn't still initialized; if you try to retrieve the
name, it will automatically initialize the __attr__ dictionary, and
try to retrieve the value of the 'name' key; as it doesnt exist, it
retrieves the default value.

The class code is also useful. If you refer to Person.name, it will
return the attribute itself, which allows one to retrieve its own
name:

obj = Person.name
print obj.name

> 
> >             if (isinstance(fobj,StringType) or
> >                 isinstance(fobj,IntType) or
> >                 isinstance(fobj,FloatType) or
> >                 isinstance(fobj,ListType)):
> 
> isinstance accepts a tuple as its 2nd arg exactly to avoid this need;
> set once, somewhere,
> 
> elementaryTypes = StringType, IntType, FloatType, ListType

This is one of the old artifacts. I had four different tests, and for
each one of them I had a special subclass: ListAttribute,
StrAttribute, and so on. It went this way because at first I only
wrote the StrAttribute, then added another one... And I'm doing it
XP-style, "the simplest code that works". At some point I finally
managed to 'get' descriptors, and implemented the TypedAttribute
class. I simply collapsed the tests into a single one (I didn't even
retype them! just deleted the intermediate lines and "and'ed" them
together; now talk about "the simplest code that works" :-)).

In fact, there are several parts of the code that are artifacts of
some experiments that I did. I was trying to figure out how to
properly use metaclasses, descriptors, properties, a lot of stuff.
Some of the old code is still there. I also kept debug code, and I
intend to... this is something that still bothers me and is a good
topic for another thread... Debug code clutters the main code, but I
don't like to have to add it back if I ever need it. (what about
logging support at the language level, pretty much as a assert, that
can be removed from optimized code?)

I have finished right now a unittest for the metatemplate module,
including inheritance testing. Now I can start to remove the old cruft
step by step without fear that everything will break apart :-)

 > and then you can typecheck here with just
> 
>               if isinstance(fobj, elementaryTypes):
> 
> ...but if this, or the recommendation to follow PEP 8 (space after
> comma, etc) is the most important suggestion I have to offer about your
> code, I guess that's a good sign!-)

I re-read PEP8 a few days ago. Some things are a matter of personal
style, and a little harder to change; for instance, I prefer to align
sequences of assignments vertically, something that Guido reportedly
loathes. But I'm already adhering to some recommendations, such as
right-margin and docstrings format.

-- 
Carlos Ribeiro
Consultoria em Projetos
blog: http://rascunhosrotos.blogspot.com
blog: http://pythonnotes.blogspot.com
mail: carribeiro at gmail.com
mail: carribeiro at yahoo.com



More information about the Python-list mailing list