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

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


当前回答

这是我在这里找到的一个很好的Python食谱:http://code.activestate.com/recipes/577024-yet-another-enum-for-python/

def enum(typename, field_names):
    "Create a new enumeration type"

    if isinstance(field_names, str):
        field_names = field_names.replace(',', ' ').split()
    d = dict((reversed(nv) for nv in enumerate(field_names)), __slots__ = ())
    return type(typename, (object,), d)()

使用示例:

STATE = enum('STATE', 'GET_QUIZ, GET_VERSE, TEACH')

更多细节可以在食谱页面上找到。

其他回答

以下是我认为有价值的方法:

允许>和<基于枚举中的顺序进行比较,而不是词法顺序 可以地址项目的名称,属性或索引:x.a, x['a']或x[0] 支持[:]或[-1]等切片操作

最重要的是防止不同类型的枚举之间的比较!

基于http://code.activestate.com/recipes/413486-first-class-enums-in-python。

这里包含了许多文档测试,以说明这种方法的不同之处。

def enum(*names):
    """
SYNOPSIS
    Well-behaved enumerated type, easier than creating custom classes

DESCRIPTION
    Create a custom type that implements an enumeration.  Similar in concept
    to a C enum but with some additional capabilities and protections.  See
    http://code.activestate.com/recipes/413486-first-class-enums-in-python/.

PARAMETERS
    names       Ordered list of names.  The order in which names are given
                will be the sort order in the enum type.  Duplicate names
                are not allowed.  Unicode names are mapped to ASCII.

RETURNS
    Object of type enum, with the input names and the enumerated values.

EXAMPLES
    >>> letters = enum('a','e','i','o','u','b','c','y','z')
    >>> letters.a < letters.e
    True

    ## index by property
    >>> letters.a
    a

    ## index by position
    >>> letters[0]
    a

    ## index by name, helpful for bridging string inputs to enum
    >>> letters['a']
    a

    ## sorting by order in the enum() create, not character value
    >>> letters.u < letters.b
    True

    ## normal slicing operations available
    >>> letters[-1]
    z

    ## error since there are not 100 items in enum
    >>> letters[99]
    Traceback (most recent call last):
        ...
    IndexError: tuple index out of range

    ## error since name does not exist in enum
    >>> letters['ggg']
    Traceback (most recent call last):
        ...
    ValueError: tuple.index(x): x not in tuple

    ## enums must be named using valid Python identifiers
    >>> numbers = enum(1,2,3,4)
    Traceback (most recent call last):
        ...
    AssertionError: Enum values must be string or unicode

    >>> a = enum('-a','-b')
    Traceback (most recent call last):
        ...
    TypeError: Error when calling the metaclass bases
        __slots__ must be identifiers

    ## create another enum
    >>> tags = enum('a','b','c')
    >>> tags.a
    a
    >>> letters.a
    a

    ## can't compare values from different enums
    >>> letters.a == tags.a
    Traceback (most recent call last):
        ...
    AssertionError: Only values from the same enum are comparable

    >>> letters.a < tags.a
    Traceback (most recent call last):
        ...
    AssertionError: Only values from the same enum are comparable

    ## can't update enum after create
    >>> letters.a = 'x'
    Traceback (most recent call last):
        ...
    AttributeError: 'EnumClass' object attribute 'a' is read-only

    ## can't update enum after create
    >>> del letters.u
    Traceback (most recent call last):
        ...
    AttributeError: 'EnumClass' object attribute 'u' is read-only

    ## can't have non-unique enum values
    >>> x = enum('a','b','c','a')
    Traceback (most recent call last):
        ...
    AssertionError: Enums must not repeat values

    ## can't have zero enum values
    >>> x = enum()
    Traceback (most recent call last):
        ...
    AssertionError: Empty enums are not supported

    ## can't have enum values that look like special function names
    ## since these could collide and lead to non-obvious errors
    >>> x = enum('a','b','c','__cmp__')
    Traceback (most recent call last):
        ...
    AssertionError: Enum values beginning with __ are not supported

LIMITATIONS
    Enum values of unicode type are not preserved, mapped to ASCII instead.

    """
    ## must have at least one enum value
    assert names, 'Empty enums are not supported'
    ## enum values must be strings
    assert len([i for i in names if not isinstance(i, types.StringTypes) and not \
        isinstance(i, unicode)]) == 0, 'Enum values must be string or unicode'
    ## enum values must not collide with special function names
    assert len([i for i in names if i.startswith("__")]) == 0,\
        'Enum values beginning with __ are not supported'
    ## each enum value must be unique from all others
    assert names == uniquify(names), 'Enums must not repeat values'

    class EnumClass(object):
        """ See parent function for explanation """

        __slots__ = names

        def __iter__(self):
            return iter(constants)

        def __len__(self):
            return len(constants)

        def __getitem__(self, i):
            ## this makes xx['name'] possible
            if isinstance(i, types.StringTypes):
                i = names.index(i)
            ## handles the more normal xx[0]
            return constants[i]

        def __repr__(self):
            return 'enum' + str(names)

        def __str__(self):
            return 'enum ' + str(constants)

        def index(self, i):
            return names.index(i)

    class EnumValue(object):
        """ See parent function for explanation """

        __slots__ = ('__value')

        def __init__(self, value):
            self.__value = value

        value = property(lambda self: self.__value)

        enumtype = property(lambda self: enumtype)

        def __hash__(self):
            return hash(self.__value)

        def __cmp__(self, other):
            assert self.enumtype is other.enumtype, 'Only values from the same enum are comparable'
            return cmp(self.value, other.value)

        def __invert__(self):
            return constants[maximum - self.value]

        def __nonzero__(self):
            ## return bool(self.value)
            ## Original code led to bool(x[0])==False, not correct
            return True

        def __repr__(self):
            return str(names[self.value])

    maximum = len(names) - 1
    constants = [None] * len(names)
    for i, each in enumerate(names):
        val = EnumValue(i)
        setattr(EnumClass, each, val)
        constants[i] = val
    constants = tuple(constants)
    enumtype = EnumClass()
    return enumtype

我喜欢使用列表或集合作为枚举。例如:

>>> packet_types = ['INIT', 'FINI', 'RECV', 'SEND']
>>> packet_types.index('INIT')
0
>>> packet_types.index('FINI')
1
>>>

我使用元类来实现枚举(在我的想法中,它是一个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()

这个解决方案是获取定义为列表的枚举类的简单方法(没有更多烦人的整数赋值):

enumeration.py:

import new

def create(class_name, names):
    return new.classobj(
        class_name, (object,), dict((y, x) for x, y in enumerate(names))
    )

example.py:

import enumeration

Colors = enumeration.create('Colors', (
    'red',
    'orange',
    'yellow',
    'green',
    'blue',
    'violet',
))

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