Docstrings for class attributes

George Sakkis george.sakkis at gmail.com
Tue Sep 23 19:24:34 EDT 2008


On Sep 23, 3:55 pm, Gerard flanagan <grflana... at gmail.com> wrote:
> George Sakkis wrote:
> > On Sep 23, 1:23 am, "Tom Harris" <celephi... at gmail.com> wrote:
>
> >> Greetings,
>
> >> I want to have a class as a container for a bunch of symbolic names
> >> for integers, eg:
>
> >> class Constants:
> >>     FOO = 1
> >>     BAR = 2
>
> >> Except that I would like to attach a docstring text to the constants,
> >> so that help(Constants.FOO) will print some arbitrary string. Sort of
> >> a very limited implementation of PEP 224. The only solution that I can
> >> see is to subclass int.__new__(), since once I have an int all it's
> >> attributes are immutable.
>
> > Here's one approach, using metaclasses and descriptors; it sort of
> > works, but it's less than ideal, both in usage and implementation.
>
> > George
>
> > #====== usage ============================================
>
> > class MyConstants:
> >     __metaclass__ = ConstantsMeta
> >     FOO = const(1, 'some docs about foo')
> >     BAR = const(2)
>
> > print MyConstants.FOO.__doc__
> > help(MyConstants.FOO)
> > print MyConstants.FOO - MyConstants.BAR
> > print MyConstants.FOO - 2
> > print 1 - MyConstants.BAR
>
> > #======= implementation ===================================
>
> > def ConstantsMeta(name, bases, namespace):
> >     for name,attr in namespace.iteritems():
> >         if isinstance(attr, const):
> >             namespace[name] = _ConstDescriptor(name, attr.value,
> > attr.doc)
> >     return type(name, bases, namespace)
>
> > class const(object):
> >     def __init__(self, value, doc=None):
> >         self.value = value
> >         self.doc = doc
>
> > class _ConstDescriptor(object):
> >     def __init__(self, name, value, doc):
> >         cls = type(name, (), dict(
> >             __doc__ = doc,
> >             __add__ = lambda self,other: value+other,
> >             __sub__ = lambda self,other: value-other,
> >             # ...
> >             __radd__ = lambda self,other: other+value,
> >             __rsub__ = lambda self,other: other-value,
> >             # XXX lots of boilerplate code for all special methods
> > follow...
> >             # XXX Is there a better way ?
> >         ))
> >         self._wrapper = cls()
>
> >     def __get__(self, obj, type):
> >         return self._wrapper
> > --
> >http://mail.python.org/mailman/listinfo/python-list
>
> I think you get an equivalent result if you forget the descriptor
> and adapt the metaclass:
>
> def ConstantsMeta(name, bases, namespace):
>      for name,attr in namespace.iteritems():
>          if isinstance(attr, const):
>              cls = type(name, (type(attr.value),), {'__doc__': attr.doc})
>              namespace[name] = cls(attr.value)
>      return type(name, bases, namespace)

Sweet! The big improvement of course is that you thought of
subclassing the type of attr.value instead of delegating manually all
special methods. It seems so obvious in retrospect, but I totally
missed it for some reason; thanks!

George



More information about the Python-list mailing list