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

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


当前回答

有趣的是,前几天我正好需要这个,但我找不到一个值得使用的实现……所以我自己写了:

import functools

class EnumValue(object):
    def __init__(self,name,value,type):
        self.__value=value
        self.__name=name
        self.Type=type
    def __str__(self):
        return self.__name
    def __repr__(self):#2.6 only... so change to what ever you need...
        return '{cls}({0!r},{1!r},{2})'.format(self.__name,self.__value,self.Type.__name__,cls=type(self).__name__)

    def __hash__(self):
        return hash(self.__value)
    def __nonzero__(self):
        return bool(self.__value)
    def __cmp__(self,other):
        if isinstance(other,EnumValue):
            return cmp(self.__value,other.__value)
        else:
            return cmp(self.__value,other)#hopefully their the same type... but who cares?
    def __or__(self,other):
        if other is None:
            return self
        elif type(self) is not type(other):
            raise TypeError()
        return EnumValue('{0.Name} | {1.Name}'.format(self,other),self.Value|other.Value,self.Type)
    def __and__(self,other):
        if other is None:
            return self
        elif type(self) is not type(other):
            raise TypeError()
        return EnumValue('{0.Name} & {1.Name}'.format(self,other),self.Value&other.Value,self.Type)
    def __contains__(self,other):
        if self.Value==other.Value:
            return True
        return bool(self&other)
    def __invert__(self):
        enumerables=self.Type.__enumerables__
        return functools.reduce(EnumValue.__or__,(enum for enum in enumerables.itervalues() if enum not in self))

    @property
    def Name(self):
        return self.__name

    @property
    def Value(self):
        return self.__value

class EnumMeta(type):
    @staticmethod
    def __addToReverseLookup(rev,value,newKeys,nextIter,force=True):
        if value in rev:
            forced,items=rev.get(value,(force,()) )
            if forced and force: #value was forced, so just append
                rev[value]=(True,items+newKeys)
            elif not forced:#move it to a new spot
                next=nextIter.next()
                EnumMeta.__addToReverseLookup(rev,next,items,nextIter,False)
                rev[value]=(force,newKeys)
            else: #not forcing this value
                next = nextIter.next()
                EnumMeta.__addToReverseLookup(rev,next,newKeys,nextIter,False)
                rev[value]=(force,newKeys)
        else:#set it and forget it
            rev[value]=(force,newKeys)
        return value

    def __init__(cls,name,bases,atts):
        classVars=vars(cls)
        enums = classVars.get('__enumerables__',None)
        nextIter = getattr(cls,'__nextitr__',itertools.count)()
        reverseLookup={}
        values={}

        if enums is not None:
            #build reverse lookup
            for item in enums:
                if isinstance(item,(tuple,list)):
                    items=list(item)
                    value=items.pop()
                    EnumMeta.__addToReverseLookup(reverseLookup,value,tuple(map(str,items)),nextIter)
                else:
                    value=nextIter.next()
                    value=EnumMeta.__addToReverseLookup(reverseLookup,value,(str(item),),nextIter,False)#add it to the reverse lookup, but don't force it to that value

            #build values and clean up reverse lookup
            for value,fkeys in reverseLookup.iteritems():
                f,keys=fkeys
                for key in keys:
                    enum=EnumValue(key,value,cls)
                    setattr(cls,key,enum)
                    values[key]=enum
                reverseLookup[value]=tuple(val for val in values.itervalues() if val.Value == value)
        setattr(cls,'__reverseLookup__',reverseLookup)
        setattr(cls,'__enumerables__',values)
        setattr(cls,'_Max',max([key for key in reverseLookup] or [0]))
        return super(EnumMeta,cls).__init__(name,bases,atts)

    def __iter__(cls):
        for enum in cls.__enumerables__.itervalues():
            yield enum
    def GetEnumByName(cls,name):
        return cls.__enumerables__.get(name,None)
    def GetEnumByValue(cls,value):
        return cls.__reverseLookup__.get(value,(None,))[0]

class Enum(object):
    __metaclass__=EnumMeta
    __enumerables__=None

class FlagEnum(Enum):
    @staticmethod
    def __nextitr__():
        yield 0
        for val in itertools.count():
            yield 2**val

def enum(name,*args):
    return EnumMeta(name,(Enum,),dict(__enumerables__=args))

接受或离开它,它做了我需要它做的:)

像这样使用它:

class Air(FlagEnum):
    __enumerables__=('None','Oxygen','Nitrogen','Hydrogen')

class Mammals(Enum):
    __enumerables__=('Bat','Whale',('Dog','Puppy',1),'Cat')
Bool = enum('Bool','Yes',('No',0))

其他回答

Alec Thomas简洁回答的一个变体(支持获取枚举值的名称):

class EnumBase(type):
    def __init__(self, name, base, fields):
        super(EnumBase, self).__init__(name, base, fields)
        self.__mapping = dict((v, k) for k, v in fields.iteritems())
    def __getitem__(self, val):
        return self.__mapping[val]

def enum(*seq, **named):
    enums = dict(zip(seq, range(len(seq))), **named)
    return EnumBase('Enum', (), enums)

Numbers = enum(ONE=1, TWO=2, THREE='three')
print Numbers.TWO
print Numbers[Numbers.ONE]
print Numbers[2]
print Numbers['three']

我需要在pyparsing中使用一些符号常量来表示二进制运算符的左右结合性。我像这样使用类常量:

# an internal class, not intended to be seen by client code
class _Constants(object):
    pass


# an enumeration of constants for operator associativity
opAssoc = _Constants()
opAssoc.LEFT = object()
opAssoc.RIGHT = object()

现在当客户端代码想要使用这些常量时,他们可以使用以下方法导入整个枚举:

import opAssoc from pyparsing

枚举是唯一的,它们可以用'is'而不是'=='来测试,它们不会在我的代码中占用很大的空间,而且它们很容易导入到客户端代码中。它们不支持任何花哨的str()行为,但到目前为止,这属于YAGNI类别。

从Python 3.4开始,正式支持枚举。您可以在Python 3.4文档页面上找到文档和示例。

枚举是使用类语法创建的,这使得它们很容易 读和写。中描述了另一种创建方法 功能的API。定义枚举,子类Enum如下所示:

from enum import Enum
class Color(Enum):
     red = 1
     green = 2
     blue = 3

这是我在这里找到的一个很好的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')

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

我用什么:

class Enum(object):
    def __init__(self, names, separator=None):
        self.names = names.split(separator)
        for value, name in enumerate(self.names):
            setattr(self, name.upper(), value)
    def tuples(self):
        return tuple(enumerate(self.names))

使用方法:

>>> state = Enum('draft published retracted')
>>> state.DRAFT
0
>>> state.RETRACTED
2
>>> state.FOO
Traceback (most recent call last):
   File "<stdin>", line 1, in <module>
AttributeError: 'Enum' object has no attribute 'FOO'
>>> state.tuples()
((0, 'draft'), (1, 'published'), (2, 'retracted'))

这就给出了整数常数,比如状态。PUBLISHED和在Django模型中用作选项的二元组。