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

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


当前回答

对于旧的Python 2.x

def enum(*sequential, **named):
    enums = dict(zip(sequential, [object() for _ in range(len(sequential))]), **named)
    return type('Enum', (), enums)

如果你命名它,是你的问题,但如果不创建对象而不是值允许你这样做:

>>> DOG = enum('BARK', 'WALK', 'SIT')
>>> CAT = enum('MEOW', 'WALK', 'SIT')
>>> DOG.WALK == CAT.WALK
False

当使用这里的其他实现时(在我的例子中使用命名实例时),必须确保永远不要尝试比较来自不同枚举的对象。这里有一个可能的陷阱:

>>> DOG = enum('BARK'=1, 'WALK'=2, 'SIT'=3)
>>> CAT = enum('WALK'=1, 'SIT'=2)
>>> pet1_state = DOG.BARK
>>> pet2_state = CAT.WALK
>>> pet1_state == pet2_state
True

呵!

其他回答

Alexandru对枚举使用类常量的建议效果很好。

我还喜欢为每组常量添加字典,以查找人类可读的字符串表示。

这有两个目的:a)它提供了一种简单的方法来漂亮地打印你的枚举;b)字典在逻辑上对常量进行分组,以便您可以测试成员关系。

class Animal:    
  TYPE_DOG = 1
  TYPE_CAT = 2

  type2str = {
    TYPE_DOG: "dog",
    TYPE_CAT: "cat"
  }

  def __init__(self, type_):
    assert type_ in self.type2str.keys()
    self._type = type_

  def __repr__(self):
    return "<%s type=%s>" % (
        self.__class__.__name__, self.type2str[self._type].upper())

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(names):
    "Create a simple enumeration having similarities to C."
    return type('enum', (), dict(map(reversed, enumerate(
        names.replace(',', ' ').split())), __slots__=()))()

使用它非常简单,只需使用包含想要引用的名称的字符串调用函数即可。

grade = enum('A B C D F')
state = enum('awake, sleeping, dead')

这些值只是整数,因此如果需要可以利用它(就像在C语言中一样)。

>>> grade.A
0
>>> grade.B
1
>>> grade.F == 4
True
>>> state.dead == 2
True

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

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