c enum - how to do this in python?
Greg Chapman
glc at well.com
Sat Feb 22 13:57:25 EST 2003
On Fri, 21 Feb 2003 22:26:08 +0100, Klaus Meyer <km-news1 at onlinehome.de> wrote:
>C has enum to define named constant like:
>enum { M0, M1, M2, MCOUNT };
>(MCOUNT gives the number of constants, nice for loops or so)
>
>How to do this in Python?
The Python source distribution includes an old metaclass demo showing one way of
doing enums in Python. I just rewrote this using new (2.2) metaclasses, which I
think makes it a little easier to understand (see below). Basically, the only
things changed are that EnumInstances are subclasses of int (which makes them
similar to True and False) and EnumMetaClasses have a __call__ method. The
latter is included so that given something like:
class Color(Enum):
red = 1
blue = 2
green = 3
Color(1) will return the EnumInstance for Color.red (and not a new instance of
the class Color).
-----begin Enum.py-----------------------------------------------------
"""Enumeration metaclass.
"""
Enum = None #initialize before definition
class EnumMetaClass(type):
"""Metaclass for enumeration.
To define your own enumeration, do something like
class Color(Enum):
red = 1
green = 2
blue = 3
Now, Color.red, Color.green and Color.blue behave totally
different: they are enumerated values, not integers.
Enumerations cannot be instantiated; however they can be
subclassed.
"""
def __new__(meta, name, bases, dict):
"""Constructor -- create an enumeration.
Called at the end of the class statement. The arguments are
the name of the new class, a tuple containing the base
classes, and a dictionary containing everything that was
entered in the class' namespace during execution of the class
statement. In the above example, it would be {'red': 1,
'green': 2, 'blue': 3}.
"""
if Enum:
for base in bases:
if not issubclass(base, Enum):
raise TypeError, \
"Enumeration base class must be enumeration"
items = []
for key, value in dict.items():
if not key.startswith('_'):
dict[key] = item = EnumInstance(name, key, value)
items.append(item)
dict['_items_'] = items
dict['_bases_'] = [base for base in bases if base is not Enum]
return super(EnumMetaClass, meta).__new__(meta, name, bases, dict)
def __repr__(self):
s = self.__name__
if self._bases_:
s = s + '(' + ", ".join(map(lambda x: x.__name__,
self._bases_)) + ')'
if self._items_:
list = []
for item in self._items_:
list.append("%s: %s" % (item.__name__, int(item)))
s = "%s: {%s}" % (s, ", ".join(list))
return s
def __call__(self, value):
mro = self.__mro__
for cls in mro:
if cls is Enum:
break
items = cls._items_
try:
i = items.index(value)
except ValueError:
pass
else:
return items[i]
raise ValueError("%r is not a member of %s" % (value, self.__name__))
class EnumInstance(int):
"""Class to represent an enumeration value.
EnumInstance('Color', 'red', 12) prints as 'Color.red' and behaves
like the integer 12.
"""
def __new__(cls, classname, enumname, value):
self = super(EnumInstance, cls).__new__(cls, value)
self.__classname = classname
self.__enumname = enumname
return self
__name__ = property(lambda s: s.__enumname)
def __repr__(self):
return "EnumInstance(%s, %s, %s)" % (`self.__classname`,
`self.__enumname`,
`int(self)`)
def __str__(self):
return "%s.%s" % (self.__classname, self.__enumname)
# Create the base class for enumerations.
# It is an empty enumeration.
class Enum(object):
__metaclass__ = EnumMetaClass
def _test():
class Color(Enum):
red = 1
green = 2
blue = 3
print Color.red
print dir(Color)
print Color(1)
print Color.red == Color.red
print Color.red == Color.blue
print Color.red == 1
print Color.red == 2
class ExtendedColor(Color):
white = 0
orange = 4
yellow = 5
purple = 6
black = 7
print ExtendedColor.orange
print ExtendedColor.red
print Color.red == ExtendedColor.red
class OtherColor(Enum):
white = 4
blue = 5
class MergedColor(Color, OtherColor):
pass
print MergedColor.red
print MergedColor.white
print Color
print ExtendedColor
print OtherColor
print MergedColor
if __name__ == '__main__':
_test()
More information about the Python-list
mailing list