Welcome, guest | Sign In | My Account | Store | Cart

Will Ware posted some enum code [ http://groups.google.com/groups?hl=en&newwindow=1&selm=GIJJzq.Jx1%40world.std.com ] a while ago, which I started playing with. I am now using a modified version mainly for error numbers instead of an Error class. It's just very nice not to worry about values.

Python, 112 lines
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
#
# enum.py
#
#	This file contains the enum class, which implements
#       C style enums for python
#

class EnumException(Exception): pass
class InvalidEnumVal(EnumException): pass
class InvalidEnum(EnumException): pass
class DuplicateEnum(EnumException): pass
class DuplicateEnumVal(EnumException): pass

class enum:
    def __init__(self, enumstr):
        self.lookup = { }
        self.reverseLookup = { }
        evalue = 0

        elist = enumstr.split(',')

        for e in elist:
            item = e.strip().split('=')

            ename = item[0].strip()
            if ename == '':
                continue

            if len(item) == 2:
                try:
                    evalue = int(item[1].strip(), 0)
                except ValueError:
                    raise InvalidEnumVal, 'Invalid value for: ' + ename
            elif len(item) != 1:
                raise InvalidEnum, "Invalid enum: " + e

            if self.lookup.has_key(ename):
                raise DuplicateEnum, "Duplicate enum name: " + ename
            if self.reverseLookup.has_key(evalue):
                raise DuplicateEnumVal,"Duplicate value %d for %s"%(evalue,ename)

            self.lookup[ename] = evalue
            self.reverseLookup[evalue] = ename
            evalue += 1

    def __getattr__(self, attr):
        return self.lookup[attr]

    def __len__(self):
        return len(self.lookup)

    def __repr__(self):
        s = ''
        values = self.lookup.values()
        values.sort()
        for e in values:
            s = s + '%s = %d\n' % (self.reverseLookup[e], e)
        return s

def main():
    str = """
JETTA,
RABBIT,
BEETLE,
THING=400,
PASSAT,
GOLF,
CABRIO=700,
EUROVAN,
"""
    v = enum(str)
    print v
    print 'PASSAT = %d' % v.PASSAT

    e1 = enum('TEST,,TEST2')
    print 'e1 len = %d' % len(e1)
    print e1

    try:
        e2 = enum('TEST,TEST1=jjj')
    except InvalidEnumVal, msg:
        print 'Invalid Enum Value Passed'
        print '    %s' % msg
    else:
        print 'Invalid Enum Value Failed'

    try:
        e2 = enum('TEST,TEST1=76=87=KJK')
    except InvalidEnum, msg:
        print 'Invalid Enum Passed'
        print '    %s' % msg
    else:
        print 'Invalid Enum Failed'

    try:
        e2 = enum('TEST,TEST')
    except DuplicateEnum, msg:
        print 'Duplicate Enum Passed'
        print '    %s' % msg
    else:
        print 'Duplicate Enum Failed'

    try:
        e2 = enum('TEST,TEST1=0')
    except DuplicateEnumVal, msg:
        print 'Duplicate Enum Val Passed'
        print '    %s' % msg
    else:
        print 'Duplicate Enum Val Failed'

if __name__ == "__main__":
    main()

It seems common to use a class to store the mapping between error names and error values. However, it seems very cumbersome to maintain the class when errors are added, moved around, or deleted. It would be very nice not to worry about values. Instead of:

class Error: onerror = 1 anothererror = 2 theother = 3 bigone = 24 baderror = 25

etc.

I'd prefer to have

errors = """ onerror=1, anothererror, theother, bigone = 24, baderror, """ Error = enum(errors)

and let the enum class worry about the values.

I was always reluctant to using enums because enumerations are just a convenience and I didn't want to add any more overhead. I ran into Will Ware's post in comp.lang.python about enums and decided to try it. I thought that the list arguments to the enum class were too cumbersome for my taste, and therefore, updated it to use a string instead. Later on, I removed as much as possible from the class to keep the overhead down.

The enum class simply creates a dictionary, just like the Error class would've, but it automatically assigns the values. The enum class ends up parsing the string and creating copies of what it needs (through strip()). I place the big error string and the call to enum in its own file, and thus, the memory used by the big string ends up going out of scope and freed at collection time.

The main thing I like about this implementation is that when the enum string is between multi-line string delimiters, editing the enum definition is identical to editing C code, except for comments. I thought about parsing comments but decided against it because of the additional overhead.

1 comment

Radek Kanovsky 19 years, 6 months ago  # | flag

Simple solution with python2.2 __metaclass__.

class _meta_enum (type) :
    def __init__ (cls, name, bases, di) :
        vdict = {}
        for v in di.itervalues() :
            vdict[v] = 1
        max = 0
        for k,v in di.iteritems() :
            if v == None :
                while 1 :
                    if not vdict.has_key(max) :
                        setattr(cls, k, max)
                        vdict[max] = 1
                        max += 1
                        break
                    max += 1
            else :
                vdict[v] = 1
        return cls

# Empty enum
class enum :
    __metaclass__ = _meta_enum


# Test class
class myenum (enum) :
    foo = None
    bar = 0


print myenum.foo # prints 1
print myenum.bar # prints 2

The _meta_enum.__init__ needs check uniqueness of values and should acquire inherited values from eventual parent classes!

Created by Luis P Caamano on Thu, 11 Oct 2001 (PSF)
Python recipes (4591)
Luis P Caamano's recipes (1)

Required Modules

  • (none specified)

Other Information and Tasks