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

sec*_*ean 1146 python enums python-3.x

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

我怎样才能在Python中表示Enum的等价物?

Ale*_*mas 2610

PEP 435中所述,已将枚举添加到Python 3.4中.在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中).

  • 要使用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)
Run Code Online (Sandbox Code Playgroud)

或等效地:

class Animal(Enum):
    ant = 1
    bee = 2
    cat = 3
    dog = 4
Run Code Online (Sandbox Code Playgroud)

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

def enum(**enums):
    return type('Enum', (), enums)
Run Code Online (Sandbox Code Playgroud)

使用方式如下:

>>> Numbers = enum(ONE=1, TWO=2, THREE='three')
>>> Numbers.ONE
1
>>> Numbers.TWO
2
>>> Numbers.THREE
'three'
Run Code Online (Sandbox Code Playgroud)

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

def enum(*sequential, **named):
    enums = dict(zip(sequential, range(len(sequential))), **named)
    return type('Enum', (), enums)
Run Code Online (Sandbox Code Playgroud)

并使用如下:

>>> Numbers = enum('ZERO', 'ONE', 'TWO')
>>> Numbers.ZERO
0
>>> Numbers.ONE
1
Run Code Online (Sandbox Code Playgroud)

可以通过以下方式添加对将值转换回名称的支持:

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)
Run Code Online (Sandbox Code Playgroud)

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

>>> Numbers.reverse_mapping['three']
'THREE'
Run Code Online (Sandbox Code Playgroud)

  • 哦,我注意到可以做出轻微的改进.`zip(x,range(len(x))`是一个代码气味 - 它可以被枚举替换为`enums = dict((x,i)代表i,x代表枚举(顺序)),**命名)``不构造zip/range所有的那些列表. (14认同)
  • 巧妙的解决方案!我想知道在调试时是否可以/如何打印标识符的名称?例如,有'print'值是%r"%Numbers.ONE`输出`值是ONE`而不是`值是1` ... (10认同)
  • `enum`包的问题在于它的枚举类型在pickle-unpickling中无法生存.创建一个enumaration`Foo = Enum("foo","bar")`,然后比较`pickle.loads(pickle.dumps(Foo.foo))== Foo.foo`; 结果是'错误'.当你有一个多处理程序时会很痛,因为看似相等的值会停止通过相等检查. (7认同)
  • 请注意,这与C enum不太相似,因为它不能有意义地具有由`enum`返回的类型的变量/对象. (6认同)
  • 虽然值本身没有输入,但您可以键入枚举类,使它们彼此不同:`返回类型(named.get('enum_type','Enum'),(),枚举)`然后调用`enum('ONE','TWO',THREE ='three',enum_type ='Numbers')` (5认同)
  • @sastanin:这不是stdlib的enum或enum34反向端口的问题。`Foo = Enum(“ Foo”,“ foo bar”)`,然后`pickle.loads(pickle.dumps(Foo.foo))== Foo.foo`是`True`。并且,如果您设法以某种方式混淆它(例如,Bar = Enum(“ Foo”,“ foo bar”)`),则会得到“ PicklingError”而不是无声失败。 (3认同)
  • 这里应该提到 [`auto()`](https://docs.python.org/3/library/enum.html#enum.auto)。 (3认同)
  • 将您的解决方案与PEP 435进行比较可能很有价值. (2认同)
  • 我无法理解,为什么他们在方法 enum(*sequential, **named) 中传递 kwargs(**named) ?请解释一下。没有 kwargs 也能工作。我查了一下。 (2认同)

Ale*_*lcu 798

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

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

class Animal:
    DOG = 1
    CAT = 2

x = Animal.DOG
Run Code Online (Sandbox Code Playgroud)

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

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

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

print(Animal.DOG.value)
# 1

print(Animal.DOG.name)
# "DOG"
Run Code Online (Sandbox Code Playgroud)

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

class Animal(Enum):
    DOG, CAT = range(2)
Run Code Online (Sandbox Code Playgroud)

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
Run Code Online (Sandbox Code Playgroud)

  • Python默认是动态的.没有正当理由在像Python这样的语言中强制执行编译时安全性,尤其是在没有Python的情况下.另一件事......一个好的模式只有在它创建的环境中才是好的.根据您使用的工具,良好的模式也可能被取代或完全无用. (241认同)
  • 我使用它,数字被`object()`替换. (49认同)
  • 不,这是一个类变量. (48认同)
  • @Longpoke如果你有100个值,那么你肯定做错了;)我喜欢与我的枚举相关联的数字......它们易于编写(vs字符串),可以很容易地保存在数据库中,并且兼容C/C++枚举,使编组更容易. (19认同)
  • 最初的PEP354不再仅仅被拒绝,而是现在被标记为已被取代.PEP435为Python 3.4添加了标准的Enum.见http://www.python.org/dev/peps/pep-0435/ (9认同)
  • `X = object()`是不方便的,因为它不知道它是什么(你只能比较namespace.X),并且有风险因为copy.deepcopy()或serialization/deserialiaztion创建一个新的,而不是等于你定义的任何一个!数字至少是安全的,但字符串通常更好. (7认同)
  • 定义数值(1和2)有什么意义?它们似乎毫无用处,这就是为什么我更喜欢zacherates的解决方案. (3认同)
  • 使用范围通常更好 (3认同)
  • 用字典更好地实现这一点,以便将动物类型与其序数值相匹配更容易吗? (3认同)
  • @AlexandruNedelcu,每个到这个页面寻找这个问题答案的人都有充分的理由用枚举来强制执行有效的值.如何在修订版之间修改常量值时,如何强制执行A​​PI,提高可读性,改进IDE界面或限制效果? (3认同)
  • 从示例中可以清楚地看出,常量被定义.但是,类型安全存在问题,这可能会导致问题. (2认同)

sha*_*pan 312

这是一个实现:

class Enum(set):
    def __getattr__(self, name):
        if name in self:
            return name
        raise AttributeError
Run Code Online (Sandbox Code Playgroud)

这是它的用法:

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

print(Animals.DOG)
Run Code Online (Sandbox Code Playgroud)

  • 优秀.这可以通过重写`__setattr __(self,name,value)`和`__delattr __(self,name)`进一步改进,这样如果你不小心写了`Animals.DOG = CAT`,它就不会默默地成功. (48认同)
  • @shahjapan:有趣但相对缓慢:对每个访问进行测试,如`Animals.DOG`; 另外,constats的值是字符串,因此与这些常量的比较比例如允许整数作为值更慢. (13认同)
  • @AndréTerra:你如何检查``try-except``块中的集合成员资格? (7认同)
  • @shahjapan:我认为这个解决方案并不像Alexandru或Mark的简短解决方案那样清晰.不过,这是一个有趣的解决方案.:) (3认同)

Mar*_*son 202

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

dog, cat, rabbit = range(3)
Run Code Online (Sandbox Code Playgroud)

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

dog, cat, rabbit, horse, *_ = range(100)
Run Code Online (Sandbox Code Playgroud)

  • 但这可能需要更多内存! (3认同)
  • 我数不清了 加星标的占位符也能解决我的财务问题吗? (3认同)

Ash*_*ppa 126

最适合您的解决方案取决于您对假货的 要求enum.

简单的枚举:

如果您enum只需要一个识别不同项目名称列表,Mark Harrison(上图)的解决方案很棒:

Pen, Pencil, Eraser = range(0, 3)
Run Code Online (Sandbox Code Playgroud)

使用a range还允许您设置任何起始值:

Pen, Pencil, Eraser = range(9, 12)
Run Code Online (Sandbox Code Playgroud)

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

class Stationery:
    Pen, Pencil, Eraser = range(0, 3)
Run Code Online (Sandbox Code Playgroud)

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

stype = Stationery.Pen
Run Code Online (Sandbox Code Playgroud)

复杂的枚举:

对于冗长的枚举列表或更复杂的枚举用法,这些解决方案是不够的.您可以通过Python Cookbook中发布的Will Ware for Simulate Enumerations查看配方.这方面的一个在线版本,请点击这里.

更多信息:

PEP 354:Python中的枚举具有 Python中枚举的提议的有趣细节以及它被拒绝的原因.

  • 使用`range`,如果它为0,你可以省略第一个参数 (7认同)

Aar*_*paa 78

在Java JDK 5之前使用的类型安全枚举模式具有许多优点.就像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
Run Code Online (Sandbox Code Playgroud)

python-dev最近的一个帖子指出,野外有几个枚举库,包括:

  • 我认为这是一个非常糟糕的方法.Animal.DOG = Animal("dog")Animal.DOG2 = Animal("dog")断言Animal.DOG == Animal.DOG2失败...... (16认同)
  • "你的枚举值不会无意中比较等于小整数的优势"这有什么好处?将枚举与整数进行比较有什么问题?特别是如果将枚举存储在数据库中,通常希望将其存储为整数,因此您必须在某些时候将其与整数进行比较. (13认同)
  • @Confusion用户不应该调用构造函数,甚至构造函数都是一个实现细节的事实,你必须与谁使用你的代码沟通,使新的枚举值没有意义,并且退出代码不会"做正确的事".当然,这并不能阻止你实现Animal.from_name("dog") - > Animal.DOG. (11认同)
  • @AaronMcSmooth这真的取决于你是从C语言进入"Enums只是几个整数的名字"还是更加面向对象的方法,其中枚举值是实际对象并且有方法(这是Java中的枚举方式) 1.5是,并且类型安全枚举模式的目的是).就个人而言,我不喜欢switch语句,所以我倾向于实际对象的枚举值. (4认同)
  • @Aaaron Maenpaa.正确.它仍然是一种破碎而过于复杂的方式. (3认同)
  • 现在尝试做Flags.FLAG1 | Flags.FLAG2,哦,它不起作用! (2认同)

Zoe*_*tic 60

Enum类可以是单行.

class Enum(tuple): __getattr__ = tuple.index
Run Code Online (Sandbox Code Playgroud)

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

>>> 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)]
Run Code Online (Sandbox Code Playgroud)


roy*_*yal 49

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

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

    class __metaclass__(type):
        def __getattr__(self, name):
            return self.values.index(name)
Run Code Online (Sandbox Code Playgroud)

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

>>> Animal.Cat
2
Run Code Online (Sandbox Code Playgroud)

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

def name_of(self, i):
    return self.values[i]
Run Code Online (Sandbox Code Playgroud)


dF.*_*dF. 47

Python没有内置的等价物enum,其他答案也有实现自己的想法(你可能也对Python食谱中的顶级版本感兴趣).

但是,在enumC中需要调用的情况下,我通常最终只使用简单的字符串:由于实现了对象/属性的方式,(C)Python被优化为无论如何都能用短字符串快速工作,所以不会使用整数真的有任何性能优势.为了防止打字错误/无效值,您可以在选定位置插入支票.

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

def take_for_a_walk(animal):
    assert animal in ANIMALS
    ...
Run Code Online (Sandbox Code Playgroud)

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

  • 我更喜欢这个解决方案 我喜欢尽可能使用内置类型. (2认同)

Dan*_*gen 37

在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
Run Code Online (Sandbox Code Playgroud)

表示:

>>> print(Color.red)
Color.red
>>> print(repr(Color.red))
<Color.red: 1>
Run Code Online (Sandbox Code Playgroud)

迭代:

>>> for color in Color:
...   print(color)
...
Color.red
Color.green
Color.blue
Run Code Online (Sandbox Code Playgroud)

程序化访问:

>>> Color(1)
Color.red
>>> Color['blue']
Color.blue
Run Code Online (Sandbox Code Playgroud)

有关更多信息,请参阅提案.官方文档很快就会发布.


mba*_*768 31

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

class Animal:
  class Dog: pass
  class Cat: pass

x = Animal.Dog
Run Code Online (Sandbox Code Playgroud)

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

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


小智 29

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()
Run Code Online (Sandbox Code Playgroud)

像这样使用它:

Animal = enum('DOG', 'CAT')
Animal.DOG # returns 0
Animal.CAT # returns 1
Animal.DOG = 2 # raises NotImplementedError
Run Code Online (Sandbox Code Playgroud)

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

__metaclass__ = M_add_class_attribs(enumerate(names))
Run Code Online (Sandbox Code Playgroud)

有了这个:

__metaclass__ = M_add_class_attribs((object(), name) for name in names)
Run Code Online (Sandbox Code Playgroud)

  • 恕我直言,如果你把`enum(names)`改为`enum(*names)`那么它会更清晰 - 然后你可以在调用它时删除额外的括号. (11认同)

dgu*_*lia 21

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

months = {
    'January': 1,
    'February': 2,
    ...
}
Run Code Online (Sandbox Code Playgroud)

要么

months = dict(
    January=1,
    February=2,
    ...
)
Run Code Online (Sandbox Code Playgroud)

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

mymonth = months['January']
Run Code Online (Sandbox Code Playgroud)

还有其他选项,如元组列表或元组元组,但字典是唯一一个为您提供"符号"(常量字符串)方式来访问该值的方法.

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


agf*_*agf 21

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

from collections import namedtuple

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

MyEnum = enum('FOO', 'BAR', 'BAZ')
Run Code Online (Sandbox Code Playgroud)

或者,或者,

# 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())
Run Code Online (Sandbox Code Playgroud)

像上面的子类一样set,这允许:

'FOO' in MyEnum
other = MyEnum.FOO
assert other == MyEnum.FOO
Run Code Online (Sandbox Code Playgroud)

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

MyEnum.FOO < MyEnum.BAR
Run Code Online (Sandbox Code Playgroud)

如果您使用填充序列号值的版本,则按预期行事.


Saš*_*jak 19

从Python 3.4开始,官方将支持枚举.您可以在Python 3.4文档页面上找到文档和示例.

枚举是使用类语法创建的,这使它们易于读写.Functional API中描述了另一种创建方法.要定义枚举,请将Enum子类分配如下:

from enum import Enum
class Color(Enum):
     red = 1
     green = 2
     blue = 3
Run Code Online (Sandbox Code Playgroud)


小智 17

我用的是什么:

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))
Run Code Online (Sandbox Code Playgroud)

如何使用:

>>> 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'))
Run Code Online (Sandbox Code Playgroud)

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


小智 15

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

months = set('January', 'February', ..., 'December')
Run Code Online (Sandbox Code Playgroud)

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

if m in months:
Run Code Online (Sandbox Code Playgroud)

但就像dF一样,我通常只使用字符串常量来代替枚举.


ste*_*eha 14

这是我见过的最好的:"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)
Run Code Online (Sandbox Code Playgroud)


dan*_*r89 14

把事情简单化:

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

    def __getattr__(self, name):
            return self.tupleList.index(name)
Run Code Online (Sandbox Code Playgroud)

然后:

DIRECTION = Enum(('UP', 'DOWN', 'LEFT', 'RIGHT'))
DIRECTION.DOWN
1
Run Code Online (Sandbox Code Playgroud)


Sin*_*ion 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
... 
Run Code Online (Sandbox Code Playgroud)

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

>>> 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
Run Code Online (Sandbox Code Playgroud)

主要特点:

  • str(),int()并且repr()都产生尽可能最有用的输出,分别是enumartion的名称,它的整数值,以及计算回枚举的Python表达式.
  • 构造函数返回的枚举值严格限制为预定义值,不包含偶然的枚举值.
  • 枚举值是单例; 他们可以严格比较is


Ria*_*zvi 11

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

>>> from enum import Enum
Run Code Online (Sandbox Code Playgroud)

但是,现在开始使用它可以安装激发PEP 的原始库:

$ pip install flufl.enum
Run Code Online (Sandbox Code Playgroud)

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

>>> 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
Run Code Online (Sandbox Code Playgroud)


est*_*ani 10

def enum(*sequential, **named):
    enums = dict(zip(sequential, [object() for _ in range(len(sequential))]), **named)
    return type('Enum', (), enums)
Run Code Online (Sandbox Code Playgroud)

如果你命名它,是你的问题,但如果没有创建对象而不是值允许你这样做:

>>> DOG = enum('BARK', 'WALK', 'SIT')
>>> CAT = enum('MEOW', 'WALK', 'SIT')
>>> DOG.WALK == CAT.WALK
False
Run Code Online (Sandbox Code Playgroud)

当使用此处的其他实现时(在我的示例中使用命名实例时),您必须确保永远不会尝试比较来自不同枚举的对象.对于这里可能存在的陷阱:

>>> 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
Run Code Online (Sandbox Code Playgroud)

哎呀!


bj0*_*bj0 9

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

def enum(**enums):
    '''simple constant "enums"'''
    return type('Enum', (object,), enums)
Run Code Online (Sandbox Code Playgroud)

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

通过对该功能的一点修改,我们可以让它更加"笨拙":

注意:我通过尝试重现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
Run Code Online (Sandbox Code Playgroud)

这将创建一个基于指定类型的枚举.除了像上一个函数一样提供属性访问之外,它的行为与您期望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
Run Code Online (Sandbox Code Playgroud)

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

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'
Run Code Online (Sandbox Code Playgroud)


pyt*_*hor 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'
Run Code Online (Sandbox Code Playgroud)


Chr*_*son 6

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

  • 允许 > 和 < 基于枚举顺序的比较,而不是词法顺序
  • 可以按名称、属性或索引寻址项目:xa、x['a'] 或 x[0]
  • 支持切片操作,如 [:] 或 [-1]

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

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

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

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
Run Code Online (Sandbox Code Playgroud)


小智 5

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())
Run Code Online (Sandbox Code Playgroud)


aba*_*ert 5

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

\n\n

截至 2013 年 4 月,似乎有一个普遍的共识:只要人们能够就“某些东西”应该是什么达成一致,就应该在 3.4\xe2\x80\x94 中向标准库添加一些东西。这是最困难的部分。请参阅从此处此处开始的主题,以及 2013 年初的六个其他主题。

\n\n

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

\n


Raf*_*fay 5

这是我在这里找到的一个很好的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)()
Run Code Online (Sandbox Code Playgroud)

用法示例:

STATE = enum('STATE', 'GET_QUIZ, GET_VERSE, TEACH')
Run Code Online (Sandbox Code Playgroud)

更多详细信息可以在食谱页面上找到。