just like "True" and "False" are in Python - and we'd like they to behave
like named constants - not just a variable pointing to an int.
So, if the constant you want has an integer value, something that behaves like
Python's True and False is easily achievable with:
class Constant(int):
def __new__(cls, name, value=0):
return int.__new__(cls, value)
def __init__(self, name, value):
self.name = name
def __repr__(self):
return self.name
__str__ = __repr__
And there you are:
>>> RED = Constant("RED", 0)
>>> RED
RED
>>> RED + 1
1
>>> print RED
RED
Then ...we stumble on the DRY principle. The constant is nice,
but the call for its creation requires its name as a string, and
we have to associate the resulting object with a variable with the same name.
It is a problem faced by "collections.namedtuple", and ORM's all
around the Galaxy.We could make the code in the Constant class to inject the constant name in the caller frame, yes. But that is ugly, as it kills the "explicit is better than implicit" principle, and would break static code analysis tools everywhere - one can't have code that fiddles with the calling frames and don't feel too hackish for production code anyway:
from inspect import currentframe
class Constant(int):
def __new__(cls, name, value=0):
return int.__new__(cls, value)
def __init__(self, name, value):
self.name = name
currentframe().f_back.f_locals[name] = self
def __repr__(self):
return self.name
__str__ = __repr__
And now one can do:
>>> Constant("GREEN", 1)
GREEN
>>> GREEN
GREEN
>>> print (GREEN, GREEN + 0)
GREEN 1
No more WET stuff. But unsactisfactory, nonetheless... Then you think on the ridle: what is that can inject clean names in the current namespace? Assignment statements, also some statements like "for", "with", "except" and so on....but also the "import" statement!
There it is -- if we can get our constants to be authomaticaly created when they are imported into the current module -- there is a drawback, the import semantics does not allow one to assign values to the names being imported.
A pity - but arbitrary constants would still work when only the name as a value is important. With you, the all new, all magic....DYNAMIC AUTO CONSTANTS
import sys
class Constant(int):
def __new__(cls, name, value=0):
return int.__new__(cls, value)
def __init__(self, name, value):
self.name = name
def __repr__(self):
return self.name
__str__ = __repr__
class _ConstantFactory(object):
counter = 0
Constant = globals()["Constant"]
def __init__(self):
self.counter = 0
self._all_constants = {}
def __getattr__(self, name):
cls = self.__class__
if name in self._all_constants:
return self._all_constants[name]
const = cls.Constant(name, self.counter)
self.counter += 1
self._all_constants[name] = const
return const
sys.modules[__name__] = _ConstantFactory()
So, this code as a not-so-well-behaved module, replaces itself in sys.modules with an instance of the _ConstantFactory class .... an almost ordinary class - but which will produce a new Constant object each time an attribute is retrueved from it - and..guess what "from <module> import name1, name2" does?>>> from ConstantFactory import RED, GREEN, BLUE >>> RED RED >>> GREEN + BLUE 5 >>> print RED, GREEN, BLUE RED GREEN BLUE
There you are: no writing names twice, no names magic appearing in the module name-space, no declarations needed.