我主要是c#开发人员,但我目前正在用Python开发一个项目。

我如何在Python中表示等价的Enum ?


当前回答

Python 2.7和find_name()

下面是所选思想的一个易于阅读的实现,其中包含一些辅助方法,这些方法可能比“reverse_mapping”更python化,使用起来更简洁。要求Python >= 2.7。

为了解决下面的一些注释,枚举对于防止代码中的拼写错误非常有用,例如对于状态机,错误分类器等。

def Enum(*sequential, **named):
  """Generate a new enum type. Usage example:

  ErrorClass = Enum('STOP','GO')
  print ErrorClass.find_name(ErrorClass.STOP)
    = "STOP"
  print ErrorClass.find_val("STOP")
    = 0
  ErrorClass.FOO     # Raises AttributeError
  """
  enums = { v:k for k,v in enumerate(sequential) } if not named else named

  @classmethod
  def find_name(cls, val):
    result = [ k for k,v in cls.__dict__.iteritems() if v == val ]
    if not len(result):
        raise ValueError("Value %s not found in Enum" % val)
    return result[0]

  @classmethod
  def find_val(cls, n):
    return getattr(cls, n)

  enums['find_val'] = find_val
  enums['find_name'] = find_name
  return type('Enum', (), enums)

其他回答

Davidg建议使用词典。我想更进一步,使用集合:

months = set('January', 'February', ..., 'December')

现在你可以测试一个值是否与集合中的一个值匹配,如下所示:

if m in months:

但是,像dF,我通常只使用字符串常量来代替枚举。

我使用元类来实现枚举(在我的想法中,它是一个const)。代码如下:

class ConstMeta(type):
    '''
    Metaclass for some class that store constants
    '''
    def __init__(cls, name, bases, dct):
        '''
        init class instance
        '''
        def static_attrs():
            '''
            @rtype: (static_attrs, static_val_set)
            @return: Static attributes in dict format and static value set
            '''
            import types
            attrs = {}
            val_set = set()
            #Maybe more
            filter_names = set(['__doc__', '__init__', '__metaclass__', '__module__', '__main__'])
            for key, value in dct.iteritems():
                if type(value) != types.FunctionType and key not in filter_names:
                    if len(value) != 2:
                        raise NotImplementedError('not support for values that is not 2 elements!')
                    #Check value[0] duplication.
                    if value[0] not in val_set:
                        val_set.add(value[0])
                    else:
                        raise KeyError("%s 's key: %s is duplicated!" % (dict([(key, value)]), value[0]))
                    attrs[key] = value
            return attrs, val_set

        attrs, val_set = static_attrs()
        #Set STATIC_ATTRS to class instance so that can reuse
        setattr(cls, 'STATIC_ATTRS', attrs)
        setattr(cls, 'static_val_set', val_set)
        super(ConstMeta, cls).__init__(name, bases, dct)

    def __getattribute__(cls, name):
        '''
        Rewrite the special function so as to get correct attribute value
        '''
        static_attrs = object.__getattribute__(cls, 'STATIC_ATTRS')
        if name in static_attrs:
            return static_attrs[name][0]
        return object.__getattribute__(cls, name)

    def static_values(cls):
        '''
        Put values in static attribute into a list, use the function to validate value.
        @return: Set of values
        '''
        return cls.static_val_set

    def __getitem__(cls, key):
        '''
        Rewrite to make syntax SomeConstClass[key] works, and return desc string of related static value.
        @return: Desc string of related static value
        '''
        for k, v in cls.STATIC_ATTRS.iteritems():
            if v[0] == key:
                return v[1]
        raise KeyError('Key: %s does not exists in %s !' % (str(key), repr(cls)))


class Const(object):
    '''
    Base class for constant class.

    @usage:

    Definition: (must inherit from Const class!
        >>> class SomeConst(Const):
        >>>   STATUS_NAME_1 = (1, 'desc for the status1')
        >>>   STATUS_NAME_2 = (2, 'desc for the status2')

    Invoke(base upper SomeConst class):
    1) SomeConst.STATUS_NAME_1 returns 1
    2) SomeConst[1] returns 'desc for the status1'
    3) SomeConst.STATIC_ATTRS returns {'STATUS_NAME_1': (1, 'desc for the status1'), 'STATUS_NAME_2': (2, 'desc for the status2')}
    4) SomeConst.static_values() returns set([1, 2])

    Attention:
    SomeCosnt's value 1, 2 can not be duplicated!
    If WrongConst is like this, it will raise KeyError:
    class WrongConst(Const):
        STATUS_NAME_1 = (1, 'desc for the status1')
        STATUS_NAME_2 = (1, 'desc for the status2')
    '''
    __metaclass__ = ConstMeta
##################################################################
#Const Base Class ends
##################################################################


def main():
    class STATUS(Const):
        ERROR = (-3, '??')
        OK = (0, '??')

    print STATUS.ERROR
    print STATUS.static_values()
    print STATUS.STATIC_ATTRS

    #Usage sample:
    user_input = 1
    #Validate input:
    print user_input in STATUS.static_values()
    #Template render like:
    print '<select>'
    for key, value in STATUS.STATIC_ATTRS.items():
        print '<option value="%s">%s</option>' % (value[0], value[1])
    print '</select>'


if __name__ == '__main__':
    main()

下面是亚历克·托马斯解决方案的一个变种:

def enum(*args, **kwargs):
    return type('Enum', (), dict((y, x) for x, y in enumerate(args), **kwargs)) 

x = enum('POOH', 'TIGGER', 'EEYORE', 'ROO', 'PIGLET', 'RABBIT', 'OWL')
assert x.POOH == 0
assert x.TIGGER == 1

Python的标准是PEP 435,所以在Python 3.4+中可以使用Enum类:

>>> from enum import Enum
>>> class Colors(Enum):
...     red = 1
...     green = 2
...     blue = 3
>>> for color in Colors: print color
Colors.red
Colors.green
Colors.blue

最好的解决方案取决于你需要从你的假枚举中得到什么。

简单的枚举:

如果你只需要枚举作为标识不同项目的名称列表,Mark Harrison(上图)的解决方案是很棒的:

Pen, Pencil, Eraser = range(0, 3)

使用范围还允许你设置任何起始值:

Pen, Pencil, Eraser = range(9, 12)

除此之外,如果你还要求项属于某种类型的容器,那么将它们嵌入到一个类中:

class Stationery:
    Pen, Pencil, Eraser = range(0, 3)

要使用枚举项,你现在需要使用容器名和项名:

stype = Stationery.Pen

复杂的枚举:

对于枚举的长列表或更复杂的enum使用,这些解决方案是不够的。你可以参考Will Ware在Python Cookbook中发布的关于在Python中模拟枚举的食谱。这里有一个在线版本。

更多信息:

PEP 354: Python中的枚举有关于Python中的枚举建议的有趣细节,以及为什么它被拒绝。