Docstrings for class attributes

117 views
Skip to first unread message

Tom Harris

unread,
Sep 23, 2008, 1:23:35 AM9/23/08
to pytho...@python.org
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.

--

Tom Harris <celephicus(AT)gmail(DOT)com>

Bruno Desthuilliers

unread,
Sep 23, 2008, 3:01:57 AM9/23/08
to
Tom Harris a écrit :

> 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

Do you have a reason to stuff them in a class ? Usually, putting them at
the top level of a module is quite enough...

> Except that I would like to attach a docstring text to the constants,
> so that help(Constants.FOO) will print some arbitrary string.

You can document them in the module or class docstring...

Diez B. Roggisch

unread,
Sep 23, 2008, 8:20:15 AM9/23/08
to
Tom Harris wrote:

Epydoc interprets strings like this as doc-strings:

"""
FOO is not a bar
"""
FOO = "foo"


However, it won't get recognized by help of course.

Diez

Peter Pearson

unread,
Sep 23, 2008, 11:14:18 AM9/23/08
to
On Tue, 23 Sep 2008 15:23:35 +1000, Tom Harris <celep...@gmail.com> wrote:
>
> 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.
[snip]

Commiserating, not helping: I have a similar problem with a module
that holds values of physical constants,
http://webpages.charter.net/curryfans/peter/nature.py:

boltzmanns_constant = 1.380622e-16 * erg / k
stefan_boltzmann_constant = 5.66961e-5 * erg/s/cm/cm/k/k/k/k
gravitational_constant = 6.6732e-8 * erg*cm/g/g

I would like to reveal more details with, e.g.,
help( gravitational_constant ) . . . and maybe then I could
use shorter names.

--
To email me, substitute nowhere->spamcop, invalid->net.

George Sakkis

unread,
Sep 23, 2008, 1:50:52 PM9/23/08
to

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

Terry Reedy

unread,
Sep 23, 2008, 2:36:29 PM9/23/08
to pytho...@python.org

gravitational_constant = g = 6.6732e-8 * erg*cm/g/g
...

Gerard flanagan

unread,
Sep 23, 2008, 3:55:30 PM9/23/08
to pytho...@python.org
> --
> 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.

George Sakkis

unread,
Sep 23, 2008, 7:24:34 PM9/23/08
to

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

Steven D'Aprano

unread,
Sep 24, 2008, 1:52:04 AM9/24/08
to

Others have suggested solutions, which may be better, but for
completeness consider using properties:

def make_const(value, doc=''):
def getter(self):
return value
return property(getter, None, None, doc)


class Foo(object):
x = make_const(1.234, 'a special number')


The only gotcha is that while help(Foo.x) works, help(Foo().x) does not.


--
Steven

Reply all
Reply to author
Forward
0 new messages