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>
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...
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
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.
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
gravitational_constant = g = 6.6732e-8 * erg*cm/g/g
...
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.
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
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