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

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


当前回答

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

其他回答

Python没有内置的enum,其他答案有实现自己的enum的想法(您可能也对Python烹饪书中的顶部版本感兴趣)。

然而,在C中调用枚举的情况下,我通常只使用简单的字符串:由于对象/属性的实现方式,(C)Python已经优化为使用短字符串工作得非常快,因此使用整数并没有任何性能上的好处。为了防止输入错误/无效值,可以在选定的位置插入检查。

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

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

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

枚举已按照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之间的代码并不完全兼容,例如,你需要在python 2中使用__order__)。

要使用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'

如果你使用MyPy,另一种表达“枚举”的方式是typing.Literal。

例如:

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


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

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

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

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

我喜欢Java枚举,这就是我在Python中的做法:

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

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

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

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

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

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

    return Enum()

示例使用:

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

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

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

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

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

class Enum(object): 
    def __init__(self, tupleList):
            self.tupleList = tupleList
    
    def __getattr__(self, name):
            return self.tupleList.index(name)

然后:

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

在使用Python 3时保持简单:

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

然后:

MyEnum.DOWN

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