[ZODB-Dev] RFC: Attributes and Options for IndexedCatalog

Greg Ward gward@mems-exchange.org
Tue, 28 Jan 2003 15:29:18 -0500


On 28 January 2003, Christian Reis said:
> Specifying fields for the catalog is currently done by using class
> attributes, and options by using _ic_* attributes. A short example
> follows:
> 
>     class Host(IndexedObject):
>         name = StringType
>         address = TupleType
>         arch = StringType
>         mhz = IntType
>         os = OpSys # a class
>         daemons = HostDaemonCollection
>         _ic_unique = ('name', 'address')
>         _ic_exclude = ('mhz',) # don't index MHz
>         _ic_weak = ('daemons',)

Using the same attribute name at class level as you use at instance
level bothers me for two reasons:

  * some people like to set the initial value of instance attributes
    via class attributes, eg.

      class Foo:
          a = None
          b = None

        def __init__ (self):
            pass

    ... so that as soon as 'self.a' is assigned, it suddenly changes
    from a class attribute to an instance attribute.  This is a
    perfectly valid and Pythonic idiom (although I personally don't
    care for it).

  * other people (eg. me) prefer to draw a very clear line between
    instance attributes and class attributes.  Your "name = StringType"
    model *is* compatible with that way of thinking: the type of 'name'
    is indeed an attribute of the class, whereas particular names are
    attributes of particular instances.

    However, doing it this way means sloppy constructors can lead to
    confusing errors.  Eg.

      class Foo:
          name = StringType

          def __init__ (self):
              self.nmae = None

    Now references to Foo().name will succeed, where they should fail
    with AttributeError because of the buggy constructor.

I think type information about instance attributes should be squeezed
into a fixed, limited number of class attributes, rather than one
type-specifying class attr per instance attr.  Therefore ...

>         class Host(IndexedObject):
>             _ic_fields = [ attr('name', StringType, unique=1),
>                            attr('address', StringType, unique=1),
>                            attr('arch', StringType),
>                            attr('mhz', IntType),
>                            attr('os', OpSys),
>                            attr('daemons', HostDaemonCollection, weak=1) ]

... I prefer this approach.

>     The problem with this approach is that it
>     removes all "naturality" from object definition; you need to use our
>     special format, which makes retrofitting classes harder.

Fiddlesticks.  A conjecture: every non-trivial Python project eventually
develops a way of expressing an object schema, ie. which attributes
belong to each class and what their types are.  Corollary: for each
non-trivial Python project, there exists a unique syntax for specifying
the object schema.  Conclusion: by adding Yet Another such syntax to the
pot, you're not making things any worse.  Also, your syntax is no worse
than the others I'm familiar with.  (Although I'm only familiar with
three, and I invented two of them.)

>     b. We could merge all existing _ic_* options into a single
>     _ic_options field. I'm in favor of this because I consider it to be
>     exceptionally clean:
> 
>         class Host:
>             name = StringType
>             address = TupleType
>             arch = StringType
>             mhz = IntType
>             os = OpSys # a class
>             daemons = HostDaemonCollection
>             _ic_options = [opt('name', unique=1),
>                            opt('address', unique=1),
>                            opt('daemons', weak=1)]

Yuck.  Not only does this require O(N) class attributes to specify the
schema (rather than the preferred O(1)), you have to specify the set of
attributes twice: once as class attributes, and then again as elements
of _ic_options.  -1 (if I get a vote).

>     c. We could kick the bucket and go for specifying a schema
>     externally to the class

Dodgy, prone to errors.

> perhaps using XML

*shudder*  I like Python because it's readable!

>     Johan says something about "generating domain class code from XML"
>     next to me and I feel worried.

"Git a rope, boys".  >grin<

        Greg
-- 
Greg Ward - software developer                gward@mems-exchange.org
MEMS Exchange                            http://www.mems-exchange.org