Python Enumeration class (revisited, again)
Don Garrett
garrett at bgb.cc
Sat Apr 19 01:14:07 EDT 2003
I wrote a Python enumeration class for my own benifit, and only
afterwards, did I go searching and see how many other people had done it.
I'm posting my main class here, just because I'd like to hear
comments about style, etc.
I'm also wondering if anyone has considered taking one of the many
different pure Python Enumeration classes that have been proposed, and
add it to the standard 'Batteries'?
A zip file of this class including a test program can be found here:
http://www.bgb.cc/garrett/originals/Enumeration.zip
But the main classes are here:
class Element(object):
"""Internal helper class used to represent an ordered abstract
value.
The values have string representations, have strictly defined
ordering (inside the set) and are never equal to anything but
themselves.
They are usually created through the Create factory method as values
for Enumerations.
They assume that the enumeration member will be filled in before any
comparisons are used. This is done by the Enumeration constructor.
"""
def __init__(self, name, value):
self.__name = name
self.__value = value
self.enumeration = None # Will be filled in later
def __repr__(self):
return self.__name
def __cmp__(self, other):
"""We override cmp only because we want the ordering of elements
in an enumeration to reflect their position in the enumeration.
"""
if (self.__class__ == other.__class__ and
self.enumeration is other.enumeration):
# If we are both elements in the same enumeration, compare
# values for ordering
return cmp(self.__value, other.__value)
else:
# Otherwise, fall back to the default
return NotImplemented
class Enumeration(tuple):
"""This class represents an enumeration. You should not normally
create multiple instances of the same enumeration, instead create
one with however many references are convenient.
The enumeration is a tuple of all of the values in it. You can
iterate through the values, perform 'in' tests, slicing, etc. It
also includes functions to lookup specific values by name, or
names by value.
You can specify your own element values, or use the Create factory
method to create default Elements. These elements are unique
system wide, and are ordered based on the order of the elements
in the enumeration. They also are _repr_'d by the name of the
element, which is convenient for testing, debugging, and generation
text output.
"""
def __new__(self, names, values):
"""This method is needed to get the tuple parent class to do the
Right Thing(tm).
"""
return tuple.__new__(self, values)
def __init__(self, names, values):
"""The arguments needed to construct this class are a list of
element names (which must be unique strings), and element values
(which can be any type of value). If you don't have special
needs, then it's recommended that you use Element instances for
the values.
This constructor is normally called through the Create factory
(which will create Elements for you), but that is not a
requirement.
"""
assert len(names) == len(values)
# We are a tuple of our values, plus more....
tuple.__init__(self, values)
self.__nameMap = {}
self.__valueMap = {}
# Store each enum value as a named attribute
self.__slots__ = names
for i in xrange(len(names)):
name = names[i]
value = values[i]
# Tell the elements which enumeration they belong too
if type(value) == Element:
value.enumeration = self
# Prove that all names are unique
assert not name in self.__nameMap
# Create mappings from name to value, and vice versa
self.__nameMap[name] = value
self.__valueMap[value] = name
# Create named attributes on ourself.
self.attachValuesTo(self)
# Force this instance to be read only
def noset(name, value): raise "No assignments allowed"
self.__setattr__ = noset
def attachValuesTo(self, other):
"""Create named values on an object to match our elements.
This is appropriate for use in a constructor, against a class
object, or a module object.
Cannot be used on a new style class instance after initialization
is complete.
"""
for name, value in self.__nameMap.items():
setattr(other, name, value)
def valueFromName(self, name):
"""Look up the enumeration value for a given element name.
"""
return self.__nameMap[name]
def nameFromValue(self, value):
"""Look up the name of an enumeration element, given it's value.
If there are multiple elements with the same value, you will
only get a single matching name back. Which name is undefined.
"""
return self.__valueMap[value]
__call__ = valueFromName
def Create(*elements):
"""Factory method for Enumerations. Accepts of list of values that
can either be strings or (name, value) tuple pairs. Strings will
have an Element created for use as their value.
Example: Enumeration.Create('fred', 'bob', ('joe', 42))
"""
names = []
values = []
for element in elements:
if type(element) == tuple:
assert len(element) == 2
names.append(element[0])
values.append(element[1])
elif type(element) in (str, unicode):
names.append(element)
values.append(Element(element, len(names)))
else:
raise "Unsupported element type"
return Enumeration(names, values)
--
Don Garrett http://www.bgb-consulting.com/garrett/
BGB Consulting dgarrett at acm.org
More information about the Python-list
mailing list