Docstrings for class attributes

Gerard flanagan grflanagan at gmail.com
Tue Sep 23 21:55:30 CEST 2008


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)

class const(object):
     def __init__(self, value, doc=None):
         self.value = value
         self.doc = doc

#====== 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

#==========================================================

Alternatively, forget the metaclass and have:

def const(name, val, doc=None):
     return type(name, (type(val),), {'__doc__': doc})(val)

#====== usage ============================================

class MyConstants:
     FOO = const('FOO', 1, 'some docs about foo')
     BAR = const('BAR', 2)
     MOO = const('MOO', 8.0, 'all about MOO')

#==========================================================

G.




More information about the Python-list mailing list