enum in Python

Pettersen, Bjorn S BjornPettersen at fairisaac.com
Mon Sep 8 21:07:30 EDT 2003


> From: Scott David Daniels [mailto:Scott.Daniels at Acm.Org] 
> 
> David M. Cook wrote:
> 
> > In article <KF67b.33$fd2.25 at newssvr23.news.prodigy.com>, 
> > Andrew Chalk wrote:
> > 
> > 
> >>As a rank Python beginner I've used a dictionary, but 
> >>presumably there is a better way.
> > 
> > 
> > I've seen idioms like 
> > 
> > FOO, BAR, BAZ = range(3)
> > 
> > used.
> > 
> > Dave Cook
> 
> For 2.3 or after:
> 
>      class Enumerate(object):
>          def __init__(self, names):
>              for number, name in enumerate(names.split()):
>                  setattr(self, name, number)
> 
> To use:
>      codes = Enumerate('FOO BAR BAZ')
>      codes.BAZ will be 2 and so on.
[...]

Which is what I would use, however, sometimes you just want to reuse
your C code, so I had a little fun creating the following... Use as:

  from enum import enum
  class LoanTypeCode(enum): 
    """
        enum LOAN_TYPE_CODE {
            UNKNOWN = 0,
            CONSTRUCTION,
            FHA,
            VHA,
            LIVESTOCK = 100,
            MACHINERY_EQUIPMENT,
            FARM_REAL_ESTATE,
            CROPS_PROCEEDS_NEW,
            CROPS_PROCEEDS_OLD,
            DAIRY_PROCEEDS,
        };
    """

  >>> print LoanTypeCode
  enum LOAN_TYPE_CODE(UNKNOWN[0], CONSTRUCTION[1], FHA[2], VHA[3],
LIVESTOCK[100], 
  MACHINERY_EQUIPMENT[101], FARM_REAL_ESTATE[102],
CROPS_PROCEEDS_NEW[103], 
  CROPS_PROCEEDS_OLD[104], DAIRY_PROCEEDS[105])

Note the beautifully discrete values :-)

  >>> print LoanTypeCode.UNKNOWN
  <LOAN_TYPE_CODE: UNKNOWN[0]>
  
.. and enum values know both their name and numberic value (and almost
where they belong...) Almost always better to display UNKNOWN than 0..

-- bjorn

--- enum.py -----
def _docStringData(strRep):
    """Pull out relevant information from docstring... Hex values etc.
left as
       an exercise.
    """
    import re
    _name = ''
    _vals = []
    m = re.match(r'\s*enum\s*([\w_]+[\w\d_]*)\s*{\s*', strRep)
    if m:
        _name = m.group(1)
        strRep = strRep[m.end():]
        matches = re.findall(r'([\w_]+[\w\d_]*)(\s*=\s*(\d+))?', strRep)
        curval = 0
        for name, _, val in matches:
            if not val:
                curval += 1
            else:
                curval = int(val)
            _vals.append((name, curval))
        return (_name, _vals)
    
    raise ValueError("The input header doesn't look like an enum: %s" %
strRep)

class _enumItem(object):
    """Object to represent individual enum values."""
    __slots__ = ['_enumName', '_name', '_val']
    
    name = property(lambda self: self._name)
    val = property(lambda self: self._val)
    
    def __init__(self, enumName, name, value):
        self._enumName, self._name, self._val = enumName, name, value
    def __repr__(self):
        return '<%s: %s[%s]>' % (self._enumName, self.name,
str(self.val))
    def __str__(self):
        return '%s.%s' % (self._enumName, self.name)
    def __cmp__(self, other):
        return cmp(self.val, other.val)

class _enum(type):
    """Enum metaclass..."""
    def __new__(cls, name, bases, clsdict):
        if name in ('enum', '_enum'): # skip metaclass machinery
            return type.__new__(cls, name, bases, clsdict)
        
        if '__doc__' in clsdict:
            if '__init__' in clsdict:
                raise TypeError("Enums can't have __init__ methods")
                
            docstr = clsdict['__doc__']
            className, enumVals = _docStringData(docstr)
            _enumvals_ = {}
            clsdict['__enumvals__'] = _enumvals_
            for name, val in enumVals:
                item = _enumItem(className, name, val)
                _enumvals_[name] = item
                clsdict[name] = property(lambda
s,name=name:s.__enumvals__[name])
                
            klassdef = type.__new__(cls, className, bases, clsdict)
            return klassdef() # oops <wink>
            
        raise TypeError("class %s must have a doc string to be an enum"
% name)
    
class enum(object):
    __metaclass__ = _enum
    
    def __str__(self):
        return self.__class__.__name__
        
    def __repr__(self):
        header = "enum " + str(self) + '('
        # sort by values
        vals = self.__enumvals__.items()
        vals.sort(lambda x,y: cmp(x[1],y[1]))
        strvals = [name + '[' + str(val._val) + ']' for name, val in
vals]
        return header + ', '.join(strvals) + ')'


        





More information about the Python-list mailing list