Docstrings for class attributes

T

Tom Harris

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.
 
B

Bruno Desthuilliers

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...
 
D

Diez B. Roggisch

Tom said:
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.

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
 
P

Peter Pearson

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.
 
G

George Sakkis

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
 
T

Terry Reedy

Peter said:
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.

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

Gerard flanagan

George said:
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

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.
 
G

George Sakkis

Here's one approach, using metaclasses and descriptors; it sort of
works, but it's less than ideal, both in usage and implementation.

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

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
 
S

Steven D'Aprano

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.



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.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,777
Messages
2,569,604
Members
45,225
Latest member
Top Crypto Podcasts

Latest Threads

Top