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

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

有帮助吗?

解决方案

枚举已添加到 Python 3.4,如中所述 公众号 435. 。也曾被 向后移植到 3.3、3.2、3.1、2.7、2.6、2.5 和 2.4 在 pypi 上。

对于更高级的枚举技术,请尝试 埃努姆图书馆 (2.7、3.3+,同一作者 enum34. 。py2 和 py3 之间的代码不完全兼容,例如你需要 __order__ 在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'

其他提示

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

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

class Animal:
    DOG = 1
    CAT = 2

x = Animal.DOG

在 Python 3.4 中(公众号 435), 你(们)能做到 枚举 基类。这将为您带来一些额外的功能,如 PEP 中所述。例如,枚举成员与整数不同,它们由 name 和一个 value.

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 实施 可以转换为列表并且是可迭代的. 。其成员的顺序是声明顺序,与其值无关。例如:

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)

最适合您的解决方案取决于您的需求 伪造的 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 中模拟枚举 发表于 Python 食谱. 。其在线版本可用 这里.

更多信息:

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

Java Pre-JDK 5中使用的类型AFE枚举模式具有许多优势。就像Alexandru的答案中一样,您创建一个类别和类级字段是枚举值。但是,枚举值是班级的实例,而不是小整数。这具有一个优势,即您的枚举价值不会无意间比较与小整数相等,您可以控制它们的打印方式,如果有用并使用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 上的线程 指出有一些枚举库,包括:

枚举类可以是单行的。

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 没有内置的等效项 enum, ,其他答案有实现您自己的想法(您可能也对 顶级版本 在 Python 食谱中)。

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

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

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

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

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

2013年5月10日,Guido同意接受 公众号 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

它比使用整数更能防止错误,因为您不必担心确保整数是唯一的(例如如果你说 Dog = 1 和 Cat = 1 你就完蛋了)。

它比使用字符串更能防止错误,因为您不必担心拼写错误(例如x == "catt" 会默默失败,但 x == Animal.Catt 是运行时异常)。

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)

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

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

或者

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

然后,您可以使用常量的符号名称,如下所示:

mymonth = months['January']

还有其他选项,例如元组列表或元组,但是词典是唯一为您提供“符号”(常数字符串)访问该值的方法。

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

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

如果您使用填充连续数值的版本,则按预期行事。

我用的是:

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 3.4 开始,将正式支持枚举。您可以找到文档和示例 在 Python 3.4 文档页面上.

使用类语法创建枚举,这使其易于读写。一种替代创建方法在功能API中描述。要定义枚举,请按如下方式对 Enum 进行子类化:

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

davidg 建议使用字典。我会更进一步并使用集合:

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

把事情简单化:

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

我曾经需要一个 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() 所有这些都会产生尽可能最有用的输出,分别是枚举的名称、其整数值以及计算回枚举的 Python 表达式。
  • 构造函数返回的枚举值严格限制为预定义值,没有意外的枚举值。
  • 枚举值是单例;它们可以严格地与 is

我真的很喜欢亚历克·托马斯的解决方案(http://stackoverflow.com/a/1695250):

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

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

通过对函数进行一些修改,我们可以让它表现得更加“枚举”:

笔记:我通过尝试重现Pygtk的新样式“ Enums”的行为(例如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

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

例如,整数枚举:

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

哎呀!

Python 的新标准是 公众号 435, ,因此 Enum 类将在未来版本的 Python 中可用:

>>> from enum import Enum

但是,要立即开始使用它,您可以安装 原始库 这激发了政治公众人物:

$ pip install flufl.enum

然后你 可以按照其在线指南使用它:

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

枚举包来自 皮伊 提供了健壮的枚举实现。较早的答案提到了 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'

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

这是一种具有一些不同特征的方法,我认为很有价值:

  • 允许 > 和 < 基于枚举中的顺序而不是词汇顺序进行比较
  • 可以按名称、属性或索引来寻址项目: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

这是一个变体 亚历克·托马斯的解决方案:

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

虽然最初的枚举提案, 公众号 354, ,几年前就被拒绝了,但现在又不断出现。某种枚举本来打算添加到 3.2 中,但它被推迟到 3.3,然后被遗忘。现在有一个 公众号 435 旨在包含在 Python 3.4 中。PEP 435 的参考实现是 flufl.enum.

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

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

一个变体(支持获取枚举值的名称) 亚历克·托马斯的简洁回答:

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']
许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top