我怎样才能用Python代表'Enum'?

时间:2008-08-31 15:55:48

标签: python python-3.x enums

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

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

43 个答案:

答案 0 :(得分:2529)

PEP 435中所述,已在Python 3.4中添加了枚举。在pypi上也是backported to 3.3, 3.2, 3.1, 2.7, 2.6, 2.5, and 2.4

对于更高级的Enum技术,请尝试aenum library(2.7,3.3 +,同一作者enum34。代码在py2和py3之间不完全兼容,例如,您需要__order__ in python 2 )。

  • 要使用enum34,请执行$ pip install enum34
  • 要使用aenum,请执行$ pip install aenum

安装enum(无号码)将安装完全不同且不兼容的版本。


from enum import Enum     # for enum34, or the stdlib version
# from aenum import Enum  # for the aenum version
Animal = Enum('Animal', 'ant bee cat dog')

Animal.ant  # returns <Animal.ant: 1>
Animal['ant']  # returns <Animal.ant: 1> (string lookup)
Animal.ant.name  # returns 'ant' (inverse lookup)

或等效地:

class Animal(Enum):
    ant = 1
    bee = 2
    cat = 3
    dog = 4

在早期版本中,完成枚举的一种方法是:

def enum(**enums):
    return type('Enum', (), enums)

使用如下:

>>> Numbers = enum(ONE=1, TWO=2, THREE='three')
>>> Numbers.ONE
1
>>> Numbers.TWO
2
>>> Numbers.THREE
'three'

您还可以使用以下内容轻松支持自动枚举:

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

并像这样使用:

>>> Numbers = enum('ZERO', 'ONE', 'TWO')
>>> Numbers.ZERO
0
>>> Numbers.ONE
1

支持将值转换回名称可以这样添加:

def enum(*sequential, **named):
    enums = dict(zip(sequential, range(len(sequential))), **named)
    reverse = dict((value, key) for key, value in enums.iteritems())
    enums['reverse_mapping'] = reverse
    return type('Enum', (), enums)

这会覆盖具有该名称的任何内容,但它对于在输出中呈现您的枚举非常有用。如果反向映射不存在,它将抛出KeyError。第一个例子:

>>> Numbers.reverse_mapping['three']
'THREE'

答案 1 :(得分:776)

在PEP 435之前,Python没有等效的,但你可以实现自己的。

我自己,我喜欢保持简单(我在网上看过一些非常复杂的例子),就像这样......

class Animal:
    DOG = 1
    CAT = 2

x = Animal.DOG

在Python 3.4(PEP 435)中,您可以将Enum作为基类。这为您提供了一些额外的功能,如PEP中所述。例如,枚举成员与整数不同,它们由namevalue组成。

class Animal(Enum):
    DOG = 1
    CAT = 2

print(Animal.DOG)
# <Animal.DOG: 1>

print(Animal.DOG.value)
# 1

print(Animal.DOG.name)
# "DOG"

如果您不想键入值,请使用以下快捷方式:

class Animal(Enum):
    DOG, CAT = range(2)

Enum个实施can be converted to lists and are iterable。其成员的顺序是声明顺序,与其值无关。例如:

class Animal(Enum):
    DOG = 1
    CAT = 2
    COW = 0

list(Animal)
# [<Animal.DOG: 1>, <Animal.CAT: 2>, <Animal.COW: 0>]

[animal.value for animal in Animal]
# [1, 2, 0]

Animal.CAT in Animal
# True

答案 2 :(得分:307)

这是一个实现:

class Enum(set):
    def __getattr__(self, name):
        if name in self:
            return name
        raise AttributeError

以下是它的用法:

Animals = Enum(["DOG", "CAT", "HORSE"])

print(Animals.DOG)

答案 3 :(得分:197)

如果你需要数值,这是最快的方法:

dog, cat, rabbit = range(3)

在Python 3.x中,您还可以在末尾添加一个带星号的占位符,这样可以吸收该范围的所有剩余值,以防您不介意浪费内存而无法计算:

dog, cat, rabbit, horse, *_ = range(100)

答案 4 :(得分:124)

最适合您的解决方案取决于您对 enum 的要求。

简单枚举:

如果您需要 enum 作为名称的列表来识别不同的,那么标记的解决方案哈里森(上图)很棒:

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

使用 range 还可以设置任何起始值

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

除上述内容外,如果您还要求这些项目属于某种容器,请将它们嵌入到类中:

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

要使用枚举项,您现在需要使用容器名称和项目名称:

stype = Stationery.Pen

复杂枚举:

对于冗长的枚举列表或更复杂的枚举用法,这些解决方案是不够的。您可以通过Will Ware查看 Python Cookbook 中发布的 Simulate Enumerations in Python 的配方。该在线版本可用here

更多信息:

PEP 354: Enumerations in Python包含了Python中枚举提案的有趣细节,以及它被拒绝的原因。

答案 5 :(得分:78)

在Java JDK 5之前使用的类型安全枚举模式有一个 一些优点。就像Alexandru的回答一样,你创造了一个 class和class level字段是枚举值;然而,枚举 值是类的实例而不是小整数。这有 你的枚举值不会无意中比较相等的优点 对于小整数,你可以控制它们的打印方式,随意添加 方法如果有用并使用isinstance进行断言:

class Animal:
   def __init__(self, name):
       self.name = name

   def __str__(self):
       return self.name

   def __repr__(self):
       return "<Animal: %s>" % self

Animal.DOG = Animal("dog")
Animal.CAT = Animal("cat")

>>> x = Animal.DOG
>>> x
<Animal: dog>
>>> x == 1
False

最近thread on python-dev指出,野外有一些枚举库,包括:

答案 6 :(得分:60)

Enum类可以是单行。

class Enum(tuple): __getattr__ = tuple.index

如何使用它(正向和反向查找,键,值,项目等)

>>> State = Enum(['Unclaimed', 'Claimed'])
>>> State.Claimed
1
>>> State[1]
'Claimed'
>>> State
('Unclaimed', 'Claimed')
>>> range(len(State))
[0, 1]
>>> [(k, State[k]) for k in range(len(State))]
[(0, 'Unclaimed'), (1, 'Claimed')]
>>> [(k, getattr(State, k)) for k in State]
[('Unclaimed', 0), ('Claimed', 1)]

答案 7 :(得分:48)

Python没有内置等效于enum的内容,其他答案也有实现自己的想法(您可能也对Python食谱中的over the top version感兴趣)。

但是,在C中要求enum的情况下,我通常只使用简单的字符串 来结束 :因为对象的方式/属性已实现,(C)Python已经过优化,无论如何都可以使用短字符串快速工作,因此使用整数实际上没有任何性能优势。为了防止打字错误/无效值,您可以在选定的位置插入支票。

ANIMALS = ['cat', 'dog', 'python']

def take_for_a_walk(animal):
    assert animal in ANIMALS
    ...

(与使用课程相比,一个缺点是你失去了自动完成的好处)

答案 8 :(得分:48)

所以,我同意。我们不要在Python中强制使用类型安全,但我想保护自己免受愚蠢的错误。那么我们怎么看待这个呢?

class Animal(object):
    values = ['Horse','Dog','Cat']

    class __metaclass__(type):
        def __getattr__(self, name):
            return self.values.index(name)

在定义我的枚举时,它让我免于价值冲突。

>>> Animal.Cat
2

还有另一个便利的优势:真正快速的反向查找:

def name_of(self, i):
    return self.values[i]

答案 9 :(得分:35)

在2013-05-10,Guido同意接受PEP 435进入Python 3.4标准库。这意味着Python最终内置了对枚举的支持!

Python 3.3,3.2,3.1,2.7,2.6,2.5和2.4有一个后端可用。它在Pypi上为enum34

声明:

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

表示:

>>> print(Color.red)
Color.red
>>> print(repr(Color.red))
<Color.red: 1>

迭代:

>>> for color in Color:
...   print(color)
...
Color.red
Color.green
Color.blue

程序化访问:

>>> Color(1)
Color.red
>>> Color['blue']
Color.blue

有关详细信息,请参阅the proposal。官方文件很快就会发布。

答案 10 :(得分:31)

我更喜欢用Python定义枚举:

class Animal:
  class Dog: pass
  class Cat: pass

x = Animal.Dog

它比使用整数更容易出错,因为你不必担心确保整数是唯一的(例如,如果你说Dog = 1而Cat = 1你就会被搞砸)。

它比使用字符串更容易出错,因为您不必担心拼写错误(例如 x ==“catt”无声地失败,但x == Animal.Catt是运行时异常。)

答案 11 :(得分:30)

def M_add_class_attribs(attribs):
    def foo(name, bases, dict_):
        for v, k in attribs:
            dict_[k] = v
        return type(name, bases, dict_)
    return foo

def enum(*names):
    class Foo(object):
        __metaclass__ = M_add_class_attribs(enumerate(names))
        def __setattr__(self, name, value):  # this makes it read-only
            raise NotImplementedError
    return Foo()

像这样使用:

Animal = enum('DOG', 'CAT')
Animal.DOG # returns 0
Animal.CAT # returns 1
Animal.DOG = 2 # raises NotImplementedError

如果您只想要唯一符号而不关心值,请替换此行:

__metaclass__ = M_add_class_attribs(enumerate(names))

用这个:

__metaclass__ = M_add_class_attribs((object(), name) for name in names)

答案 12 :(得分:22)

嗯......我想最接近枚举的是字典,定义如下:

months = {
    'January': 1,
    'February': 2,
    ...
}

months = dict(
    January=1,
    February=2,
    ...
)

然后,您可以使用这样的常量的符号名称:

mymonth = months['January']

还有其他选项,如元组列表或元组元组,但字典是唯一一个为您提供“符号”(常量字符串)方式的字典 值。

编辑:我也喜欢亚历山德鲁的答案!

答案 13 :(得分:22)

另一个非常简单的Python实现,使用namedtuple

from collections import namedtuple

def enum(*keys):
    return namedtuple('Enum', keys)(*keys)

MyEnum = enum('FOO', 'BAR', 'BAZ')

或者,

# With sequential number values
def enum(*keys):
    return namedtuple('Enum', keys)(*range(len(keys)))

# From a dict / keyword args
def enum(**kwargs):
    return namedtuple('Enum', kwargs.keys())(*kwargs.values())

与子类set的上述方法类似,这允许:

'FOO' in MyEnum
other = MyEnum.FOO
assert other == MyEnum.FOO

但它具有更大的灵活性,因为它可以有不同的键和值。这允许

MyEnum.FOO < MyEnum.BAR

如果您使用填写序号值的版本,则按预期行事。

答案 14 :(得分:18)

从Python 3.4开始,官方将支持枚举。您可以找到文档和示例here on Python 3.4 documentation page

  

使用类语法创建枚举,这使它们变得简单   阅读和写作。另一种创建方法描述于   功能API。要定义枚举,请按如下方式对Enum进行子类化:

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

答案 15 :(得分:18)

我用的是什么:

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'))

因此,这为您提供了像state.PUBLISHED这样的整数常量以及在Django模型中用作选项的两元组。

答案 16 :(得分:16)

davidg建议使用dicts。我更进一步,使用集合:

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

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

if m in months:
但是,像dF一样,我通常只使用字符串常量来代替枚举。

答案 17 :(得分:15)

这是我见过的最好的:“Python中的头等级枚举”

http://code.activestate.com/recipes/413486/

它为您提供了一个类,该类包含所有枚举。枚举可以相互比较,但没有任何特定的价值;你不能将它们用作整数值。 (我一开始就反对这个,因为我习惯了C枚举,这是整数值。但是如果你不能把它当作整数使用,你就不能错误地将它用作一个整数,所以整体来说我认为这是一个胜利。)每个枚举都是一个独特的值。你可以打印枚举,你可以迭代它们,你可以测试枚举值是否在枚举中“。它非常完整和光滑。

编辑(cfi):上面的链接不兼容Python 3。这是我对Python 3的enum.py端口:

def cmp(a,b):
   if a < b: return -1
   if b < a: return 1
   return 0


def Enum(*names):
   ##assert names, "Empty enums are not supported" # <- Don't like empty enums? Uncomment!

   class EnumClass(object):
      __slots__ = names
      def __iter__(self):        return iter(constants)
      def __len__(self):         return len(constants)
      def __getitem__(self, i):  return constants[i]
      def __repr__(self):        return 'Enum' + str(names)
      def __str__(self):         return 'enum ' + str(constants)

   class EnumValue(object):
      __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):
         # C fans might want to remove the following assertion
         # to make all enums comparable by ordinal value {;))
         assert self.EnumType is other.EnumType, "Only values from the same enum are comparable"
         return cmp(self.__value, other.__value)
      def __lt__(self, other):   return self.__cmp__(other) < 0
      def __eq__(self, other):   return self.__cmp__(other) == 0
      def __invert__(self):      return constants[maximum - self.__value]
      def __nonzero__(self):     return bool(self.__value)
      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


if __name__ == '__main__':
   print( '\n*** Enum Demo ***')
   print( '--- Days of week ---')
   Days = Enum('Mo', 'Tu', 'We', 'Th', 'Fr', 'Sa', 'Su')
   print( Days)
   print( Days.Mo)
   print( Days.Fr)
   print( Days.Mo < Days.Fr)
   print( list(Days))
   for each in Days:
      print( 'Day:', each)
   print( '--- Yes/No ---')
   Confirmation = Enum('No', 'Yes')
   answer = Confirmation.No
   print( 'Your answer is not', ~answer)

答案 18 :(得分:14)

保持简单:

class Enum(object): 
    def __init__(self, tupleList):
            self.tupleList = tupleList

    def __getattr__(self, name):
            return self.tupleList.index(name)

然后:

DIRECTION = Enum(('UP', 'DOWN', 'LEFT', 'RIGHT'))
DIRECTION.DOWN
1

答案 19 :(得分:13)

为了解码二进制文件格式,我有机会需要一个Enum类。我碰巧想要的功能是简洁的枚举定义,能够通过整数值或字符串自由创建枚举实例,以及有用的repr表示。这就是我最终的结果:

>>> class Enum(int):
...     def __new__(cls, value):
...         if isinstance(value, str):
...             return getattr(cls, value)
...         elif isinstance(value, int):
...             return cls.__index[value]
...     def __str__(self): return self.__name
...     def __repr__(self): return "%s.%s" % (type(self).__name__, self.__name)
...     class __metaclass__(type):
...         def __new__(mcls, name, bases, attrs):
...             attrs['__slots__'] = ['_Enum__name']
...             cls = type.__new__(mcls, name, bases, attrs)
...             cls._Enum__index = _index = {}
...             for base in reversed(bases):
...                 if hasattr(base, '_Enum__index'):
...                     _index.update(base._Enum__index)
...             # create all of the instances of the new class
...             for attr in attrs.keys():
...                 value = attrs[attr]
...                 if isinstance(value, int):
...                     evalue = int.__new__(cls, value)
...                     evalue._Enum__name = attr
...                     _index[value] = evalue
...                     setattr(cls, attr, evalue)
...             return cls
... 

使用它的一个异想天开的例子:

>>> class Citrus(Enum):
...     Lemon = 1
...     Lime = 2
... 
>>> Citrus.Lemon
Citrus.Lemon
>>> 
>>> Citrus(1)
Citrus.Lemon
>>> Citrus(5)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 6, in __new__
KeyError: 5
>>> class Fruit(Citrus):
...     Apple = 3
...     Banana = 4
... 
>>> Fruit.Apple
Fruit.Apple
>>> Fruit.Lemon
Citrus.Lemon
>>> Fruit(1)
Citrus.Lemon
>>> Fruit(3)
Fruit.Apple
>>> "%d %s %r" % ((Fruit.Apple,)*3)
'3 Apple Fruit.Apple'
>>> Fruit(1) is Citrus.Lemon
True

主要特点:

  • str()int()repr()都可以产生最有用的输出,分别是enumartion的名称,整数值和计算回枚举的Python表达式。
  • 构造函数返回的枚举值严格限制为预定义值,不是偶然的枚举值。
  • 枚举值是单身人士;可以将它们与is
  • 进行严格比较

答案 20 :(得分:10)

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

糟糕!

答案 21 :(得分:10)

Python中的新标准是PEP 435,因此未来的Python版本中将提供Enum类:

>>> from enum import Enum

然而,现在开始使用它可以安装激发PEP的original library

$ pip install flufl.enum

然后你can use it as per its online guide

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

答案 22 :(得分:10)

我非常喜欢Alec Thomas的解决方案(http://stackoverflow.com/a/1695250):

def enum(**enums):
    '''simple constant "enums"'''
    return type('Enum', (object,), enums)

它看起来优雅整洁,但它只是一个创建具有指定属性的类的函数。

通过对该功能进行一些修改,我们可以让它更加“笨拙”:

  

注意:我尝试重现了以下示例   pygtk的新风格'枚举'的行为(如Gtk.MessageType.WARNING)

def enum_base(t, **enums):
    '''enums with a base class'''
    T = type('Enum', (t,), {})
    for key,val in enums.items():
        setattr(T, key, T(val))

    return T

这将创建一个基于指定类型的枚举。除了像上一个函数一样提供属性访问之外,它的行为与您期望Enum相对于类型的行为一样。它还继承了基类。

例如,整数枚举:

>>> Numbers = enum_base(int, ONE=1, TWO=2, THREE=3)
>>> Numbers.ONE
1
>>> x = Numbers.TWO
>>> 10 + x
12
>>> type(Numbers)
<type 'type'>
>>> type(Numbers.ONE)
<class 'Enum'>
>>> isinstance(x, Numbers)
True

使用此方法可以完成的另一个有趣的事情是通过覆盖内置方法来自定义特定行为:

def enum_repr(t, **enums):
    '''enums with a base class and repr() output'''
    class Enum(t):
        def __repr__(self):
            return '<enum {0} of type Enum({1})>'.format(self._name, t.__name__)

    for key,val in enums.items():
        i = Enum(val)
        i._name = key
        setattr(Enum, key, i)

    return Enum



>>> Numbers = enum_repr(int, ONE=1, TWO=2, THREE=3)
>>> repr(Numbers.ONE)
'<enum ONE of type Enum(int)>'
>>> str(Numbers.ONE)
'1'

答案 23 :(得分:7)

来自PyPI的枚举包提供了强大的枚举实现。早先的回答提到了PEP 354;这被拒绝了,但提案已经实施 http://pypi.python.org/pypi/enum

用法简单而优雅:

>>> from enum import Enum
>>> Colors = Enum('red', 'blue', 'green')
>>> shirt_color = Colors.green
>>> shirt_color = Colors[2]
>>> shirt_color > Colors.red
True
>>> shirt_color.index
2
>>> str(shirt_color)
'green'

答案 24 :(得分:6)

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())

答案 25 :(得分:5)

这是一种具有一些我觉得有价值的不同特征的方法:

  • 允许&gt;和&lt;基于枚举顺序的比较,而不是词汇顺序
  • 可以按名称,属性或索引来处理项目: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

答案 26 :(得分:4)

这个解决方案是一种简单的方法,可以将枚举的类定义为列表(不再需要烦人的整数赋值):

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',
))

答案 27 :(得分:4)

以下是Alec Thomas's solution的变体:

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

答案 28 :(得分:4)

虽然最初的枚举提案PEP 354在几年前遭到拒绝,但它一直在回升。某种枚举本打算添加到3.2中,但它被推回到3.3然后被遗忘。现在有一个PEP 435打算包含在Python 3.4中。 PEP 435的参考实现是flufl.enum

截至2013年4月,似乎普遍认为,只要人们可以就“某事”的内容达成一致,就应该将某些添加到3.4中的标准库中。那是困难的部分。查看2013年最初几个月开始herehere以及其他六个主题的帖子。

与此同时,每次出现这种情况时,PyPI,ActiveState等都会出现大量新设计和实现,所以如果您不喜欢FLUFL设计,请尝试PyPI search

答案 29 :(得分: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')

更多详情可在食谱页面找到。

答案 30 :(得分:3)

变体(支持获取枚举值的名称)到Alec Thomas's neat answer

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']

答案 31 :(得分:3)

在答案列表中没有看到这一个,这是我掀起的那个。它允许使用'in'关键字和len()方法:

class EnumTypeError(TypeError):
    pass

class Enum(object):
    """
    Minics enum type from different languages
    Usage:
    Letters = Enum(list('abc'))
    a = Letters.a
    print(a in Letters) # True
    print(54 in Letters) # False
    """
    def __init__(self, enums):
        if isinstance(enums, dict):
            self.__dict__.update(enums)
        elif isinstance(enums, list) or isinstance(enums, tuple):
            self.__dict__.update(**dict((v,k) for k,v in enumerate(enums)))
        else:
            raise EnumTypeError

    def __contains__(self, key):
        return key in self.__dict__.values()

    def __len__(self):
        return len(self.__dict__.values())


if __name__ == '__main__':
    print('Using a dictionary to create Enum:')
    Letters = Enum(dict((v,k) for k,v in enumerate(list('abcde'))))
    a = Letters.a
    print('\tIs a in e?', a in Letters)
    print('\tIs 54 in e?', 54 in Letters)
    print('\tLength of Letters enum:', len(Letters))

    print('\nUsing a list to create Enum:')
    Letters = Enum(list('abcde'))
    a = Letters.a
    print('\tIs a in e?', a in Letters)
    print('\tIs 54 in e?', 54 in Letters)
    print('\tLength of Letters enum:', len(Letters))

    try:
        # make sure we raise an exception if we pass an invalid arg
        Failure = Enum('This is a Failure')
        print('Failure')
    except EnumTypeError:
        print('Success!')

输出:

Using a dictionary to create Enum:
        Is a in e? True
        Is 54 in e? False
        Length of Letters enum: 5

Using a list to create Enum:
        Is a in e? True
        Is 54 in e? False
        Length of Letters enum: 5
Success!

答案 32 :(得分:2)

我需要在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类别。

答案 33 :(得分:2)

为什么枚举必须是整数?不幸的是,我想不出任何好看的构造来生成这个而不改变Python语言,所以我将使用字符串:

class Enumerator(object):
    def __init__(self, name):
        self.name = name

    def __eq__(self, other):
        if self.name == other:
            return True
        return self is other

    def __ne__(self, other):
        if self.name != other:
            return False
        return self is other

    def __repr__(self):
        return 'Enumerator({0})'.format(self.name)

    def __str__(self):
        return self.name

class Enum(object):
    def __init__(self, *enumerators):
        for e in enumerators:
            setattr(self, e, Enumerator(e))
    def __getitem__(self, key):
        return getattr(self, key)

然后再说,为了配置文件或其他远程输入,我们可以自然地测试字符串。

示例:

class Cow(object):
    State = Enum(
        'standing',
        'walking',
        'eating',
        'mooing',
        'sleeping',
        'dead',
        'dying'
    )
    state = State.standing

In [1]: from enum import Enum

In [2]: c = Cow()

In [3]: c2 = Cow()

In [4]: c.state, c2.state
Out[4]: (Enumerator(standing), Enumerator(standing))

In [5]: c.state == c2.state
Out[5]: True

In [6]: c.State.mooing
Out[6]: Enumerator(mooing)

In [7]: c.State['mooing']
Out[7]: Enumerator(mooing)

In [8]: c.state = Cow.State.dead

In [9]: c.state == c2.state
Out[9]: False

In [10]: c.state == Cow.State.dead
Out[10]: True

In [11]: c.state == 'dead'
Out[11]: True

In [12]: c.state == Cow.State['dead']
Out[11]: True

答案 34 :(得分:2)

我通常使用的解决方案是获取动态创建的类的实例的简单函数。

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

答案 35 :(得分:2)

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

答案 36 :(得分:2)

这很有趣,前几天我只是需要这个,我找不到值得使用的实现...所以我自己写了:

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))

答案 37 :(得分:1)

Python 2.7和find_name()

这是一个易于阅读的选择理念的实现,带有一些辅助方法,使用它们可能比“reverse_mapping”更Pythonic和更清晰。需要Python&gt; = 2.7。

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

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)

答案 38 :(得分:1)

我喜欢Java枚举,这就是我在Python中的表现:

def enum(clsdef):
    class Enum(object):
        __slots__=tuple([var for var in clsdef.__dict__ if isinstance((getattr(clsdef, var)), tuple) and not var.startswith('__')])

        def __new__(cls, *args, **kwargs):
            if not '_the_instance' in cls.__dict__:
                cls._the_instance = object.__new__(cls, *args, **kwargs)
            return cls._the_instance

        def __init__(self):
            clsdef.values=lambda cls, e=Enum: e.values()
            clsdef.valueOf=lambda cls, n, e=self: e.valueOf(n)
            for ordinal, key in enumerate(self.__class__.__slots__):
                args=getattr(clsdef, key)
                instance=clsdef(*args)
                instance._name=key
                instance._ordinal=ordinal
                setattr(self, key, instance)

        @classmethod
        def values(cls):
            if not hasattr(cls, '_values'):
                cls._values=[getattr(cls, name) for name in cls.__slots__]
            return cls._values

        def valueOf(self, name):
            return getattr(self, name)

        def __repr__(self):
            return ''.join(['<class Enum (', clsdef.__name__, ') at ', str(hex(id(self))), '>'])

    return Enum()

样品使用:

i=2
@enum
class Test(object):
    A=("a",1)
    B=("b",)
    C=("c",2)
    D=tuple()
    E=("e",3)

    while True:
        try:
            F, G, H, I, J, K, L, M, N, O=[tuple() for _ in range(i)]
            break;
        except ValueError:
            i+=1

    def __init__(self, name="default", aparam=0):
        self.name=name
        self.avalue=aparam

所有类变量都被定义为元组,就像构造函数一样。到目前为止,您无法使用命名参数。

答案 39 :(得分:1)

def enum( *names ):

    '''
    Makes enum.
    Usage:
        E = enum( 'YOUR', 'KEYS', 'HERE' )
        print( E.HERE )
    '''

    class Enum():
        pass
    for index, name in enumerate( names ):
        setattr( Enum, name, index )
    return Enum

答案 40 :(得分:1)

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

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

答案 41 :(得分:1)

使用以下内容。

TYPE = {'EAN13':   u'EAN-13',
        'CODE39':  u'Code 39',
        'CODE128': u'Code 128',
        'i25':     u'Interleaved 2 of 5',}

>>> TYPE.items()
[('EAN13', u'EAN-13'), ('i25', u'Interleaved 2 of 5'), ('CODE39', u'Code 39'), ('CODE128', u'Code 128')]
>>> TYPE.keys()
['EAN13', 'i25', 'CODE39', 'CODE128']
>>> TYPE.values()
[u'EAN-13', u'Interleaved 2 of 5', u'Code 39', u'Code 128']

我将它用于Django模型选择,它看起来非常pythonic。它不是真正的Enum,但它确实起到了作用。

答案 42 :(得分:1)

遵循Aaron Maenpaa提出的类似Java的实现,我得到了以下内容。我们的想法是使其具有通用性和可解析性。

class Enum:
    #'''
    #Java like implementation for enums.
    #
    #Usage:
    #class Tool(Enum): name = 'Tool'
    #Tool.DRILL = Tool.register('drill')
    #Tool.HAMMER = Tool.register('hammer')
    #Tool.WRENCH = Tool.register('wrench')
    #'''

    name = 'Enum'    # Enum name
    _reg = dict([])   # Enum registered values

    @classmethod
    def register(cls, value):
        #'''
        #Registers a new value in this enum.
        #
        #@param value: New enum value.
        #
        #@return: New value wrapper instance.
        #'''
        inst = cls(value)
        cls._reg[value] = inst
        return inst

    @classmethod
    def parse(cls, value):
        #'''
        #Parses a value, returning the enum instance.
        #
        #@param value: Enum value.
        #
        #@return: Value corresp instance.        
        #'''
        return cls._reg.get(value)    

    def __init__(self, value):
        #'''
        #Constructor (only for internal use).
        #'''
        self.value = value

    def __str__(self):
        #'''
        #str() overload.
        #'''
        return self.value

    def __repr__(self):
        #'''
        #repr() overload.
        #'''
        return "<" + self.name + ": " + self.value + ">"