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').
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.