Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Docstrings for class attributes

124 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

0 new messages