如何在Python中表示'Enum'?

1141 投票
43 回答
1285129 浏览
提问于 2025-04-11 09:15

我主要是做C#开发的,但现在正在做一个Python的项目。

在Python中,我怎么能表示一个类似于枚举(Enum)的东西呢?

43 个回答

356

这里有一个实现的例子:

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

这是它的使用方法:

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

print(Animals.DOG)
977

在PEP 435之前,Python没有类似的功能,但你可以自己实现一个。

我个人喜欢简单的方式(我在网上见过一些复杂得令人头疼的例子),像这样……

class Animal:
    DOG = 1
    CAT = 2

x = Animal.DOG

在Python 3.4中(PEP 435),你可以让Enum成为基类。这样你就能获得一些额外的功能,具体内容可以在PEP中找到。例如,枚举成员和整数是不同的,它们由一个name和一个value组成。

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)

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
2990

枚举(Enums)是在Python 3.4中新增的功能,具体内容可以参考PEP 435。此外,它也被移植到了3.3、3.2、3.1、2.7、2.6、2.5和2.4版本上。

如果你想尝试更高级的枚举技巧,可以使用aenum库(适用于2.7和3.3以上版本,和enum34是同一个作者)。不过,Python 2和Python 3之间的代码并不是完全兼容,比如在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

撰写回答