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 REDThen ...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 1No 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! T
here 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.