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

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


当前回答

遵循Aaron Maenpaa提出的类Java枚举实现,我得出了以下结论。我们的想法是使它具有通用性和可解析性。

class Enum:
    #'''
    #Java like implementation for enums.
    #
    #Usage:
    #class Tool(Enum): name = 'Tool'
    #Tool.DRILL = Tool.register('drill')
    #Tool.HAMMER = Tool.register('hammer')
    #Tool.WRENCH = Tool.register('wrench')
    #'''

    name = 'Enum'    # Enum name
    _reg = dict([])   # Enum registered values

    @classmethod
    def register(cls, value):
        #'''
        #Registers a new value in this enum.
        #
        #@param value: New enum value.
        #
        #@return: New value wrapper instance.
        #'''
        inst = cls(value)
        cls._reg[value] = inst
        return inst

    @classmethod
    def parse(cls, value):
        #'''
        #Parses a value, returning the enum instance.
        #
        #@param value: Enum value.
        #
        #@return: Value corresp instance.        
        #'''
        return cls._reg.get(value)    

    def __init__(self, value):
        #'''
        #Constructor (only for internal use).
        #'''
        self.value = value

    def __str__(self):
        #'''
        #str() overload.
        #'''
        return self.value

    def __repr__(self):
        #'''
        #repr() overload.
        #'''
        return "<" + self.name + ": " + self.value + ">"

其他回答

枚举已按照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

在Java pre-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上最近的一个线程指出,在野外有几个枚举库,包括:

flufl.enum lazr.enum ... 和富有想象力的enum

我喜欢在Python中这样定义枚举:

class Animal:
  class Dog: pass
  class Cat: pass

x = Animal.Dog

这比使用整数更有漏洞,因为你不必担心确保整数是唯一的(例如,如果你说Dog = 1和Cat = 1,你就完蛋了)。

它比使用字符串更防bug,因为你不必担心拼写错误(例如。 x == "猫"无声失败,但x ==动物。Catt是一个运行时异常)。


附录: 你甚至可以通过让Dog和Cat继承一个具有正确元类的符号类来增强这个解决方案:

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

class Symbol(metaclass=SymbolClass): pass


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

然后,如果你使用这些值来索引一个字典,请求它的表示将使它们看起来很漂亮:

>>> mydict = {Animal.Dog: 'Wan Wan', Animal.Cat: 'Nyaa'}
>>> mydict
{Animal.Dog: 'Wan Wan', Animal.Cat: 'Nyaa'}

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

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

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

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

注意:我通过尝试重现 pygtk的新样式'enums'的行为(如gtk . messagtype . 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对类型的行为一样。它还继承了基类。

例如,整数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'

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

就我个人而言,我喜欢保持简单(我在网上看到过一些非常复杂的例子),就像这样……

class Animal:
    DOG = 1
    CAT = 2

x = Animal.DOG

在Python 3.4 (PEP 435)中,可以将Enum作为基类。这为您提供了一些额外的功能,详见PEP。例如,enum成员不同于整数,它们由名称和值组成。

from enum import Enum

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

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

print(Animal.DOG.value)
# 1

print(Animal.DOG.name)
# "DOG"

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

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

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

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

list(Animal)
# [<Animal.DOG: 1>, <Animal.CAT: 2>, <Animal.COW: 0>]

[animal.value for animal in Animal]
# [1, 2, 0]

Animal.CAT in Animal
# True