English 中文(简体)
如何在Python中表示枚举?
原标题:
  • 时间:2008-08-31 15:55:47
  •  标签:
This question s answers are a community effort. Edit existing answers to improve this post. It is not currently accepting new answers or interactions.

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

我如何在Python中表示等效的枚举?

最佳回答

枚举已添加到Python 3.4中,如PEP 435。它也是在pypi上后端口到3.3、3.2、3.1、2.7、2.6、2.5和2.4

有关更高级的Enum技术,请尝试aenum库(2.7,3.3+,与enum34的作者相同。代码在py2和py3之间并不完全兼容,例如您需要__order__在python 2中)。

  • To use enum34, do $ pip install enum34
  • To use aenum, do $ pip install aenum

安装<code>enum</code>(无数字)将安装一个完全不同且不兼容的版本。


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 

如果您使用MyPy,另一种表达“enums”的方式是使用键入。literals

例如:

from typing import Literal #python >=3.8
from typing_extensions import Literal #python 2.7, 3.4-3.7


Animal = Literal[ ant ,  bee ,  cat ,  dog ]

def hello_animal(animal: Animal):
    print(f"hello {animal}")

hello_animal( rock ) # error
hello_animal( bee ) # passes

问题回答

在PEP435之前,Python没有等效程序,但您可以实现自己的。

就我自己而言,我喜欢保持简单(我在网上看到了一些非常复杂的例子),诸如此类。。。

class Animal:
    DOG = 1
    CAT = 2

x = Animal.DOG

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

from enum import Enum

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)

枚举实现可以转换为列表并可迭代。其成员的顺序是声明顺序,与它们的值无关。例如:

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

以下是一种实现方式:

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

以下是它的用法:

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

print(Animals.DOG)

如果您需要数值,这里有最快的方法:

dog, cat, rabbit = range(3)

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

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

对您来说,最好的解决方案取决于您对fakeenum的要求。

简单枚举:

如果您需要enum作为识别不同项目的名称的列表,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

复杂枚举:

对于枚举的长列表或更复杂的枚举使用,这些解决方案是不够的。您可以查看Will Ware在Python Cookbook中发布的在Python中模拟枚举的食谱。可获得其在线版本此处

更多信息:

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

The typesafe enum pattern which was used in Java pre-JDK 5 has a number of advantages. Much like in Alexandru s answer, you create a class and class level fields are the enum values; however, the enum values are instances of the class rather than small integers. This has the advantage that your enum values don t inadvertently compare equal to small integers, you can control how they re printed, add arbitrary methods if that s useful and make assertions using 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

最近的python-dev上的线程指出,目前有几个枚举库,包括:

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

所以,我同意。让我们不要在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]

Python没有内置的与<code>enum</code>等价的东西,其他答案也有实现自己的想法(您可能也对<a href=“http://code.activestate.com/recipes/67107/“rel=”noreferrer“>Python食谱中的顶级版本)。

然而,在C中调用enum的情况下,我通常只使用简单字符串:由于对象/属性的实现方式,(C)Python经过优化,可以非常快速地处理短字符串,因此使用整数不会带来任何性能优势。为了防止拼写错误/无效值,您可以在选定的位置插入支票。

ANIMALS = [ cat ,  dog ,  python ]

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

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

2013年5月10日,Guido同意接受PEP 435进入Python 3.4标准库。这意味着Python终于有了对枚举的内置支持!

有一个可用于Python 3.3、3.2、3.1、2.7、2.6、2.5和2.4的后台端口。它在Pypi上作为枚举34

宣言:

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

有关详细信息,请参阅提案。官方文件可能很快就会公布。

我更喜欢在Python中定义枚举,如下所示:

class Animal:
  class Dog: pass
  class Cat: pass

x = Animal.Dog

它比使用整数更能防bug,因为你不必担心确保整数是唯一的(例如,如果你说Dog=1和Cat=1,你会被搞砸的)。

It s more bug-proof than using strings since you don t have to worry about typos (e.g. x == "catt" fails silently, but x == Animal.Catt is a runtime exception).


ADDENDUM : You can even enhance this solution by having Dog and Cat inherit from a symbol class with the right metaclass :

class SymbolClass(type):
    def __repr__(self): return self.__qualname__
    def __str__(self): return self.__name__

class Symbol(metaclass=SymbolClass): pass


class Animal:
    class Dog(Symbol): pass
    class Cat(Symbol): pass

然后,如果你使用这些值来索引字典,请求它的表示会使它们看起来很好:

>>> mydict = {Animal.Dog:  Wan Wan , Animal.Cat:  Nyaa }
>>> mydict
{Animal.Dog:  Wan Wan , Animal.Cat:  Nyaa }
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)

从Python 3.4开始,就有了对枚举的官方支持。您可以找到文档和示例此处位于Python 3.4文档页面

Enumerations are created using the class syntax, which makes them easy to read and write. An alternative creation method is described in Functional API. To define an enumeration, subclass Enum as follows:

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

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




# Example for dictionary param:
values = {"Salad": 20, "Carrot": 99, "Tomato": "No i m not"} 
Vegetables= enum(**values)

# >>> print(Vegetables.Tomato)         No i m not 


# Example for keyworded params: 
Fruits = enum(Apple="Steve Jobs", Peach=1, Banana=2)

# >>> print(Fruits.Apple)              Steve Jobs 

就像上面的方法那样,将<code>集合</code>子类化,这允许:

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

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

MyEnum.FOO < MyEnum.BAR

如果您使用的是填充序列号值的版本,则按预期操作。

保持简单,使用旧的Python 2.x(请参阅下面的Python 3!):

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

使用Python 3时请保持简单:

from enum import Enum
class MyEnum(Enum):
    UP = 1
    DOWN = 2
    LEFT = 3
    RIGHT = 4

然后:

MyEnum.DOWN

请参阅:https://docs.python.org/3/library/enum.html

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

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

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

Then, you can use the symbolic name f或 the constants like this:

mymonth = months[ January ]

There are other options, like a list of tuples, 或 a tuple of tuples, but the dictionary is the only one that provides you with a "symbolic" (constant string) way to access the value.

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

我使用的内容:

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模型中的选择。

Python中的标准是PEP 435,因此Enum类在Python 3.4+中可用:

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

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

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

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

if m in months:

不过,与dF一样,我通常只使用字符串常量来代替枚举。

这是我见过的最好的一个:“Python中的第一类枚举”

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

它为您提供了一个类,并且该类包含所有枚举。枚举可以相互比较,但没有任何特定的值;不能将它们用作整数值。(一开始我很抗拒,因为我习惯了C枚举,它们是整数值。但如果你不能把它用作整数,你就不能把它误当作整数,所以总的来说,我认为这是一个胜利。)每个枚举都是一个唯一的值。您可以打印枚举,可以对其进行迭代,还可以测试枚举值是否“在”枚举中。它相当完整和光滑。

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

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

为了解码二进制文件格式,我有时需要一个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() and repr() all produce the most useful output possible, respectively the name of the enumartion, its integer value, and a Python expression that evaluates back to the enumeration.
  • Enumerated values returned by the constructor are limited strictly to the predefined values, no accidental enum values.
  • Enumerated values are singletons; they can be strictly compared with is

对于旧的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

诶呀

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

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

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

只要对函数进行一点修改,我们就可以使它的行为更加枚举:

NOTE: I created the following examples by trying to reproduce the behavior of pygtk s new style enums (like 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 

The enum package from PyPI provides a robust implementation of enums. An earlier answer mentioned PEP 354; this was rejected but the proposal was implemented 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 

以下是一种我认为有价值的具有不同特征的方法:

  • allows > and < comparison based on order in enum, not lexical order
  • can address item by name, property or index: x.a, x[ a ] or x[0]
  • supports slicing operations like [:] or [-1]

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

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

这里包含了许多doctest来说明这种方法的不同之处。

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

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

当原始枚举提议时,PEP 354,几年前被拒绝了,它不断出现。某种枚举本应添加到3.2,但后来被推回到3.3,然后被遗忘了。现在有了PEP 435旨在包含在Python 3.4中。PEP 435的参考实现是flufl.enum

截至2013年4月,似乎有一个普遍的共识,即一些应该在3.4中添加到标准库中,只要人们能够就“东西”应该是什么达成一致。这是困难的部分。查看启动此处此处,以及2013年初的六个其他线程。

同时,每次出现这种情况时,PyPI、ActiveState等都会出现大量新的设计和实现,所以如果你不喜欢FLUFL的设计,请尝试PyPI搜索

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

更多详细信息可以在配方页面上找到。

以下是Alec Thomas的解决方案

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

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

枚举.py:

import new

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

示例.py:

import enumeration

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




相关问题