Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Python enums revisited

2 views
Skip to first unread message

Will Ware

unread,
Aug 22, 2001, 11:41:00 PM8/22/01
to
""" I once tried to give Python something like C's enums, as
described here:
http://groups.google.com/groups?selm=G6qzLy.6Fo%40world.std.com
That approach had the flaw of trying to assign to a dictionary
returned by the locals() function, intending that such assignments
would become class attributes, but that isn't guaranteed to work
in future versions of Python. The present line of thinking was
prompted by the "name-->method lookup" thread.
"""

import types, string, pprint

def attachEnums(clas, enumList):
i = 0
for x in enumList:
if type(x) == types.TupleType:
x, i = x
assert type(x) == types.StringType
assert type(i) == types.IntType # allow longs???
setattr(clas, x, i)
try: clas.enumNames[i] = x
except AttributeError: clas.enumNames = {i: x}
i = i + 1

class MidiEvent:
"""MidiEvent is a class"""
def __repr__(self):
# It's handy to be able to reverse-lookup the names of the
# enum values. C doesn't offer this feature.
return ("<MidiEvent %s>" %
(self.enumNames[self.type]))


attachEnums(MidiEvent,
["NOTE_ON",
"NOTE_OFF",
"ANOTHER_MIDI_EVENT",
"SOME_OTHER_MIDI_EVENT",
("CONTROLLER_CHANGE", 500),
("POLYPHONIC_KEY_PRESSURE", 271828),
# many many more...
])

for x in string.split(
"""
m = MidiEvent() # MidiEvent is a class, m is an instance
m.type = MidiEvent.POLYPHONIC_KEY_PRESSURE
print m

# Notice that m's attributes include neither the enums themselves nor
# the enumNames dictionary. These are CLASS attributes, not INSTANCE
# attributes, and that means you can create vast numbers of MidiEvent
# instances with reckless abandon, never worrying that namespaces and
# memory will become cluttered with redundant copies of your enums.
pprint.pprint(MidiEvent.__dict__)
print m.__dict__
""", "\n"):
if x and x[:1] != '#':
print ">>>", x
exec x
else:
print x

--
-----------------------------------+---------------------
22nd century: Esperanto, geodesic | Will Ware
domes, hovercrafts, metric system | ww...@world.std.com

Will Ware

unread,
Aug 23, 2001, 10:12:06 AM8/23/01
to
Slightly improved version, verifies that enum values are
all unique.
====================================================

""" I once tried to give Python something like C's enums, as
described here:
http://groups.google.com/groups?selm=G6qzL...@world.std.com

That approach had the flaw of trying to assign to a dictionary
returned by the locals() function, intending that such assignments
would become class attributes, but that isn't guaranteed to work
as versions of Python change.
"""

import types, string, pprint, exceptions

class EnumException(exceptions.Exception):
pass

def attachEnums(clas, enumList):
i = 0

uniqueValues = [ ]


for x in enumList:
if type(x) == types.TupleType:
x, i = x

if type(x) != types.StringType:
raise EnumException, \
"enum name is not a string: " + x
if type(i) != types.IntType:
raise EnumException, \
"enum value is not an integer: " + i
if i in uniqueValues:
raise EnumException, \
"enum value is not unique for " + x
uniqueValues.append(i)


setattr(clas, x, i)
try: clas.enumNames[i] = x
except AttributeError: clas.enumNames = {i: x}
i = i + 1

class MidiEvent:
"""MidiEvent is a class"""
def __repr__(self):
# It's handy to be able to reverse-lookup the names of the
# enum values. C doesn't offer this feature.
return ("<MidiEvent %s>" %
(self.enumNames[self.type]))


attachEnums(MidiEvent,
["NOTE_ON",
"NOTE_OFF",
"ANOTHER_MIDI_EVENT",
"SOME_OTHER_MIDI_EVENT",
("CONTROLLER_CHANGE", 500),

("POLYPHONIC_KEY_PRESSURE", 500),

Will Ware

unread,
Aug 23, 2001, 6:04:38 PM8/23/01
to
Third time's a charm, one hopes.
=======================================================================

"""I once tried to give Python something like C's enums, as described
here: http://groups.google.com/groups?selm=G6qzLy.6Fo%40world.std.com

That approach had the flaw of trying to assign to a dictionary
returned by the locals() function, intending that such assignments
would become class attributes, but that isn't guaranteed to work as
versions of Python change."""

import types, string, pprint, exceptions

class EnumException(exceptions.Exception):
pass

class Enumeration:
def __init__(self, name, enumList):
self.__doc__ = name
lookup = { }
reverseLookup = { }
i = 0
uniqueNames = [ ]


uniqueValues = [ ]
for x in enumList:
if type(x) == types.TupleType:
x, i = x
if type(x) != types.StringType:
raise EnumException, "enum name is not a string: " + x
if type(i) != types.IntType:
raise EnumException, "enum value is not an integer: " + i

if x in uniqueNames:
raise EnumException, "enum name is not unique: " + x


if i in uniqueValues:
raise EnumException, "enum value is not unique for " + x

uniqueNames.append(x)
uniqueValues.append(i)
lookup[x] = i
reverseLookup[i] = x


i = i + 1

self.lookup = lookup
self.reverseLookup = reverseLookup
def __getattr__(self, attr):
if not self.lookup.has_key(attr):
raise AttributeError
return self.lookup[attr]
def whatis(self, value):
return self.reverseLookup[value]

Volkswagen = Enumeration("Volkswagen",
["JETTA",
"RABBIT",
"BEETLE",
("THING", 400),
"PASSAT",
"GOLF",
("CABRIO", 700),
"EURO_VAN",
"CLASSIC_BEETLE",
"CLASSIC_VAN"
])

Insect = Enumeration("Insect",
["ANT",
"APHID",
"BEE",
"BEETLE",
"BUTTERFLY",
"MOTH",
"HOUSEFLY",
"WASP",
"CICADA",
"GRASSHOPPER",
"COCKROACH",
"DRAGONFLY"
])

def demo(lines):
previousLineEmpty = 0
for x in string.split(lines, "\n"):
if x:
if x[0] != '#':
print ">>>", x; exec x; print
previousLineEmpty = 1
else:
print x
previousLineEmpty = 0
elif not previousLineEmpty:
print x
previousLineEmpty = 1

def whatkind(value, enum):
return enum.__doc__ + "." + enum.whatis(value)

class ThingWithType:
def __init__(self, type):
self.type = type

demo("""
car = ThingWithType(Volkswagen.BEETLE)
print whatkind(car.type, Volkswagen)
bug = ThingWithType(Insect.BEETLE)
print whatkind(bug.type, Insect)

# Notice that car's and bug's attributes don't include any of the
# enum machinery, because that machinery is all CLASS attributes and
# not INSTANCE attributes. So you can generate thousands of cars and
# bugs with reckless abandon, never worrying that time or memory will
# be wasted on redundant copies of the enum stuff.

print car.__dict__
print bug.__dict__
pprint.pprint(Volkswagen.__dict__)
pprint.pprint(Insect.__dict__)
""")

0 new messages