问题:如何在Python中表示“枚举”?

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

如何用Python表示等效的枚举?

I’m mainly a C# developer, but I’m currently working on a project in Python.

How can I represent the equivalent of an Enum in Python?


回答 0

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)

或等效地:

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'

Enums have been added to Python 3.4 as described in PEP 435. It has also been backported to 3.3, 3.2, 3.1, 2.7, 2.6, 2.5, and 2.4 on pypi.

For more advanced Enum techniques try the aenum library (2.7, 3.3+, same author as enum34. Code is not perfectly compatible between py2 and py3, e.g. you’ll need __order__ in python 2).

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

Installing enum (no numbers) will install a completely different and incompatible version.


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)

or equivalently:

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

In earlier versions, one way of accomplishing enums is:

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

which is used like so:

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

You can also easily support automatic enumeration with something like this:

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

and used like so:

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

Support for converting the values back to names can be added this way:

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)

This overwrites anything with that name, but it is useful for rendering your enums in output. It will throw KeyError if the reverse mapping doesn’t exist. With the first example:

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

回答 1

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

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

class Animal:
    DOG = 1
    CAT = 2

x = Animal.DOG

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

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

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

Before PEP 435, Python didn’t have an equivalent but you could implement your own.

Myself, I like keeping it simple (I’ve seen some horribly complex examples on the net), something like this …

class Animal:
    DOG = 1
    CAT = 2

x = Animal.DOG

In Python 3.4 (PEP 435), you can make Enum the base class. This gets you a little bit of extra functionality, described in the PEP. For example, enum members are distinct from integers, and they are composed of a name and 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"

If you don’t want to type the values, use the following shortcut:

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

Enum implementations can be converted to lists and are iterable. The order of its members is the declaration order and has nothing to do with their values. For example:

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

这是一个实现:

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

这是它的用法:

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

print(Animals.DOG)

Here is one implementation:

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

Here is its usage:

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

print(Animals.DOG)

回答 3

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

dog, cat, rabbit = range(3)

在Python 3.x中,您还可以在末尾添加一个加星标的占位符,以防吸收内存中的剩余值,以防万一:

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

If you need the numeric values, here’s the quickest way:

dog, cat, rabbit = range(3)

In Python 3.x you can also add a starred placeholder at the end, which will soak up all the remaining values of the range in case you don’t mind wasting memory and cannot count:

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

回答 4

最好的解决方案取决于您对假货的 要求enum

简单枚举:

如果enum仅需要标识不同项目名称列表,那么马克·哈里森(上述)的解决方案非常有用:

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

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

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

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

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

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

stype = Stationery.Pen

复杂的枚举:

对于一长串的枚举或更复杂的枚举使用,这些解决方案将无法满足要求。您可以查看Will Cook 的Python食谱手册中的Python 模拟枚举方法。可在此处获得其在线版本。

更多信息:

PEP 354:Python枚举中有一个有趣的细节,建议使用Python枚举以及为什么拒绝该枚举。

The best solution for you would depend on what you require from your fake enum.

Simple enum:

If you need the enum as only a list of names identifying different items, the solution by Mark Harrison (above) is great:

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

Using a range also allows you to set any starting value:

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

In addition to the above, if you also require that the items belong to a container of some sort, then embed them in a class:

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

To use the enum item, you would now need to use the container name and the item name:

stype = Stationery.Pen

Complex enum:

For long lists of enum or more complicated uses of enum, these solutions will not suffice. You could look to the recipe by Will Ware for Simulating Enumerations in Python published in the Python Cookbook. An online version of that is available here.

More info:

PEP 354: Enumerations in Python has the interesting details of a proposal for enum in Python and why it was rejected.


回答 5

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

python-dev上的一个最新线程指出,野外有几个枚举库,包括:

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

A recent thread on python-dev pointed out there are a couple of enum libraries in the wild, including:


回答 6

枚举类可以是单行。

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

An Enum class can be a one-liner.

class Enum(tuple): __getattr__ = tuple.index

How to use it (forward and reverse lookup, keys, values, items, etc.)

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

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

So, I agree. Let’s not enforce type safety in Python, but I would like to protect myself from silly mistakes. So what do we think about this?

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

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

It keeps me from value-collision in defining my enums.

>>> Animal.Cat
2

There’s another handy advantage: really fast reverse lookups:

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

回答 8

Python没有等效于的内置函数enum,其他答案也有实现自己的想法(您可能也对Python食谱中的顶级版本感兴趣)。

但是,在enum需要用C调用an的情况下,我通常最终只使用简单的字符串:由于对象/属性的实现方式,(C)Python经过优化,无论如何都可以非常快速地使用短字符串,因此使用整数确实不会对性能产生任何好处。为了防止输入错误/无效值,您可以在所选位置插入支票。

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

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

(与使用类相比,一个缺点是您失去了自动完成功能的优势)

Python doesn’t have a built-in equivalent to enum, and other answers have ideas for implementing your own (you may also be interested in the over the top version in the Python cookbook).

However, in situations where an enum would be called for in C, I usually end up just using simple strings: because of the way objects/attributes are implemented, (C)Python is optimized to work very fast with short strings anyway, so there wouldn’t really be any performance benefit to using integers. To guard against typos / invalid values you can insert checks in selected places.

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

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

(One disadvantage compared to using a class is that you lose the benefit of autocomplete)


回答 9

在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

有关更多信息,请参阅建议。官方文档可能很快就会发布。

On 2013-05-10, Guido agreed to accept PEP 435 into the Python 3.4 standard library. This means that Python finally has builtin support for enumerations!

There is a backport available for Python 3.3, 3.2, 3.1, 2.7, 2.6, 2.5, and 2.4. It’s on Pypi as enum34.

Declaration:

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

Representation:

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

Iteration:

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

Programmatic access:

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

For more information, refer to the proposal. Official documentation will probably follow soon.


回答 10

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

class Animal:
  class Dog: pass
  class Cat: pass

x = Animal.Dog

与使用整数相比,它具有更好的防错功能,因为您不必担心确保整数是唯一的(例如,如果您说Dog = 1和Cat = 1会被搞砸)。

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

I prefer to define enums in Python like so:

class Animal:
  class Dog: pass
  class Cat: pass

x = Animal.Dog

It’s more bug-proof than using integers since you don’t have to worry about ensuring that the integers are unique (e.g. if you said Dog = 1 and Cat = 1 you’d be screwed).

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


回答 11

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

Use it like this:

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

if you just want unique symbols and don’t care about the values, replace this line:

__metaclass__ = M_add_class_attribs(enumerate(names))

with this:

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

回答 12

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

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

要么

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

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

mymonth = months['January']

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

编辑:我也喜欢Alexandru的答案!

Hmmm… I suppose the closest thing to an enum would be a dictionary, defined either like this:

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

or

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

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

mymonth = months['January']

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

Edit: I like Alexandru’s answer too!


回答 13

另一个非常简单的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

如果您使用填充连续数字值的版本,则可以按预期操作。

Another, very simple, implementation of an enum in Python, using namedtuple:

from collections import namedtuple

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

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

or, alternatively,

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

Like the method above that subclasses set, this allows:

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

But has more flexibility as it can have different keys and values. This allows

MyEnum.FOO < MyEnum.BAR

to act as is expected if you use the version that fills in sequential number values.


回答 14

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

枚举是使用类语法创建的,这使得它们易于读写。在功能API中介绍了另一种创建方法。要定义枚举,请子类Enum如下:

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

From Python 3.4 there will be official support for enums. You can find documentation and examples here on Python 3.4 documentation page.

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

回答 15

我用什么:

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模型中使用两个元组作为选择。

What I use:

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

How to use:

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

So this gives you integer constants like state.PUBLISHED and the two-tuples to use as choices in Django models.


回答 16

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

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

现在,您可以像这样测试一个值是否与集合中的值之一匹配:

if m in months:

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

davidg recommends using dicts. I’d go one step further and use sets:

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

Now you can test whether a value matches one of the values in the set like this:

if m in months:

like dF, though, I usually just use string constants in place of enums.


回答 17

这是我所见过的最好的:“ 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)

This is the best one I have seen: “First Class Enums in Python”

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

It gives you a class, and the class contains all the enums. The enums can be compared to each other, but don’t have any particular value; you can’t use them as an integer value. (I resisted this at first because I am used to C enums, which are integer values. But if you can’t use it as an integer, you can’t use it as an integer by mistake so overall I think it is a win.) Each enum is a unique value. You can print enums, you can iterate over them, you can test that an enum value is “in” the enum. It’s pretty complete and slick.

Edit (cfi): The above link is not Python 3 compatible. Here’s my port of enum.py to 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)

回答 18

把事情简单化:

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

Keep it simple:

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

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

Then:

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

回答 19

为了解码二进制文件格式,我有时需要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

I have had occasion to need of an Enum class, for the purpose of decoding a binary file format. The features I happened to want is concise enum definition, the ability to freely create instances of the enum by either integer value or string, and a useful representation. Here’s what I ended up with:

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

A whimsical example of using it:

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

Key features:

  • 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

回答 20

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

>>> from enum import Enum

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

$ 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

The new standard in Python is PEP 435, so an Enum class will be available in future versions of Python:

>>> from enum import Enum

However to begin using it now you can install the original library that motivated the PEP:

$ pip install flufl.enum

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

回答 21

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

kes!

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

If you name it, is your problem, but if not creating objects instead of values allows you to do this:

>>> DOG = enum('BARK', 'WALK', 'SIT')
>>> CAT = enum('MEOW', 'WALK', 'SIT')
>>> DOG.WALK == CAT.WALK
False

When using other implementations sited here (also when using named instances in my example) you must be sure you never try to compare objects from different enums. For here’s a possible pitfall:

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

Yikes!


回答 22

我真的很喜欢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'

I really like Alec Thomas’ solution (http://stackoverflow.com/a/1695250):

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

It’s elegant and clean looking, but it’s just a function that creates a class with the specified attributes.

With a little modification to the function, we can get it to act a little more ‘enumy’:

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

This creates an enum based off a specified type. In addition to giving attribute access like the previous function, it behaves as you would expect an Enum to with respect to types. It also inherits the base class.

For example, integer enums:

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

Another interesting thing that can be done with this method is customize specific behavior by overriding built-in methods:

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

PyPI的enum包提供了enum 的可靠实现。较早的答案提到了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'

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.

Usage is easy and elegant:

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

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

Alexandru’s suggestion of using class constants for enums works quite well.

I also like to add a dictionary for each set of constants to lookup a human-readable string representation.

This serves two purposes: a) it provides a simple way to pretty-print your enum and b) the dictionary logically groups the constants so that you can test for membership.

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

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

  • 允许基于枚举而不是词法顺序的>和<比较
  • 可以通过名称,属性或索引来寻址项目:xa,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

Here’s an approach with some different characteristics I find valuable:

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

and most importantly prevents comparisons between enums of different types!

Based closely on http://code.activestate.com/recipes/413486-first-class-enums-in-python.

Many doctests included here to illustrate what’s different about this approach.

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

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

Here is a variant on 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

回答 27

此解决方案是获取枚举类的简单方法,该类定义为列表(不再烦人的整数分配):

枚举.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',
))

This solution is a simple way of getting a class for the enumeration defined as a list (no more annoying integer assignments):

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

回答 28

虽然最初的枚举建议PEP 354在几年前被拒绝,但它仍在继续提出。本来打算将某种枚举添加到3.2,但是将其推回到3.3,然后忘记了。现在有一个PEP 435,打算包含在Python 3.4中。PEP 435的参考实现是flufl.enum

截至2013年4月,似乎已经达成了普遍共识,即应该在3.4的标准库中添加一些内容,只要人们可以就该“内容”达成共识。那是困难的部分。请参阅此处此处开始的主题以及2013年前几个月的其他六个主题。

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

While the original enum proposal, PEP 354, was rejected years ago, it keeps coming back up. Some kind of enum was intended to be added to 3.2, but it got pushed back to 3.3 and then forgotten. And now there’s a PEP 435 intended for inclusion in Python 3.4. The reference implementation of PEP 435 is flufl.enum.

As of April 2013, there seems to be a general consensus that something should be added to the standard library in 3.4—as long as people can agree on what that “something” should be. That’s the hard part. See the threads starting here and here, and a half dozen other threads in the early months of 2013.

Meanwhile, every time this comes up, a slew of new designs and implementations appear on PyPI, ActiveState, etc., so if you don’t like the FLUFL design, try a PyPI search.


回答 29

使用以下内容。

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模型选择,它看起来非常Python。它实际上不是一个枚举,但是可以完成工作。

Use the following.

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

I used that for Django model choices, and it looks very pythonic. It is not really an Enum, but it does the job.


声明:本站所有文章,如无特殊说明或标注,均为本站原创发布。任何个人或组织,在未征得本站同意时,禁止复制、盗用、采集、发布本站内容到任何网站、书籍等各类媒体平台。如若本站内容侵犯了原著者的合法权益,可联系我们进行处理。