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

This enumerated-values class is intended for use in module-level constants, such as an employee type or a status value. It allows simple attribute notation (e.g., employee.Type.Serf or employee.Type.CEO), but also supports simple bidirectional mapping using dictionary notation (e.g., employee.Type['CEO']-->1 or employee.Type[1]-->'CEO').

Python, 51 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
class EnumType( object ):
    """
    Enumerated-values class.
    
    Allows reference to enumerated values by name
    or by index (i.e., bidirectional mapping).
    """
    def __init__( self, *names ):
        # Remember names list for reference by index
        self._names = list(names)
        # Attributes for direct reference
        for _i, _s in enumerate( self._names ):
            setattr( self, _s, _i )
    
    def __contains__( self, item ):
        try:
            trans = self[item]
            return True
        except:
            return False
    
    def __iter__( self ):
        return enumerate( self._names )
    
    def __getitem__( self, key ):
        if type(key) == type(0):
            return self._names[key]
        else:
            return self._nameToEnum( key )
    
    def __len__( self ):
        return len(self._names)
    
    def items( self ):
        return [ (idx, self._names[idx])
                for idx in range(0, len(self._names) ) ]
    
    
    def names( self ):
        return self._names[:]
    
    
    def _nameToEnum( self, name ):
        try:
            return getattr( self, name )
        except ValueError, exc:
            args = list(exc.args)
            args.append( "Unknown enum value name '%s'" % name )
            args = tuple(args)
            exc.args = args
            raise

There are some other enum solutions in the Cookbook. However, they didn't seem to do what I wanted to do. I prefer to work with symbolic names (for more readable code), yet still have a simple way to translate back and forth between integer values (for fast comparisons, minimum object size, and compact database storage) and string representation (for UI presentation, especially in selection lists).

So this class simply accepts a list of strings, which become both the instance attribute names and the string representations of the values, in its constructor. __getitem__ accepts either an integer or a string, and translates from one to the other. It also allows iteration, returning a (value, name) tuple for each iteration (or, if you prefer, an items() method mimics that of dictionaries):<pre> Status = EnumType.EnumType( 'Unknown', 'Good', 'Bad' )

print "Status.Unknown =", Status.Unknown
print "Status['Unknown'] =", Status['Unknown']
print "Status[0] =", Status[0]

for i, n in Status:
    print 'Status[%d] = %r' % ( i, n )

for i, n in Status.items():
    print 'Status[%d] = %r' % ( i, n )

</pre>

I commonly use this class to define status and type codes in modules. For example:<pre> import EnumType

Status = EnumType.EnumType(
    'Unknown',  # Status unknown/not set.
    'Good',     # Data verified
    'Bad',      # Data verification failed
)

class ContactEmail( object ):
    ...
    def __init__( self, ... ):
        ...
        self.status = Status.Unknown

</pre>

In a web application, a select widget can be auto-generated from the items list:<pre> S = "<select name='Status'>" for item in ContactEmail.Status: S += '<option value='%d'>%s</option>' % item S += "</select>" </pre>

The bottom line: This class makes defining a module enumerated value a single-line operation, and makes using the enumerated value very straightforward.

Created by Samuel Reynolds on Mon, 13 Sep 2004 (PSF)
Python recipes (4591)
Samuel Reynolds's recipes (2)

Required Modules

  • (none specified)

Other Information and Tasks