我主要是一名C#开发人员,但现在正在用Python开展项目。
在Python中如何表示与枚举等效的结构?
我主要是一名C#开发人员,但现在正在用Python开展项目。
在Python中如何表示与枚举等效的结构?
枚举类型已在Python 3.4中添加,如PEP 435所述。它还被后向移植到 pypi 上的 3.3、3.2、3.1、2.7、2.6、2.5 和 2.4 。
想要更高级的枚举技巧,请尝试使用 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
class Animal:
DOG = 1
CAT = 2
x = Animal.DOG
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
object()
。 - TobuX = object()
不方便,因为它不知道自己是什么(你只能与 namespace.X 进行比较),而且风险很高,因为 copy.deepcopy() 或序列化/反序列化会创建一个新的对象,它与你定义的任何对象都不相等!数字至少是安全的,但通常最好使用字符串。 - Beni Cherniavsky-Paskin这里是一个实现:
class Enum(set):
def __getattr__(self, name):
if name in self:
return name
raise AttributeError
以下是它的用法:
Animals = Enum(["DOG", "CAT", "HORSE"])
print(Animals.DOG)
__setattr__(self, name, value)
和可能的__delattr__(self, name)
进一步改进,这样如果您意外写入Animals.DOG = CAT
,它将不会悄无声息地成功。 - Joonas PulakkaAnimals.DOG
都需要进行一次测试;而且常量的值是字符串,因此与这些常量进行比较会比如果允许使用整数值更慢。 - Eric O. Lebigot"My pet is a "+Animals.DOG
。Python3的Enum类需要一个__str__方法来删除类名,并且仍然需要一个str()转换...除非我漏掉了什么。 - shao.lo如果您需要数字值,这里是最快的方式:
dog, cat, rabbit = range(3)
在Python 3.x中,你还可以在末尾添加一个星号作为占位符,以吸收所有剩余的range值,以防你不介意浪费内存并且无法计数:
dog, cat, rabbit, horse, *_ = range(100)
对于你来说最好的解决方案将取决于你对于你的enum
的需求,它是一个假的。
简单枚举:
如果你需要enum
仅作为标识不同项目名称列表,那么Mark Harrison的解决方案(上面的)非常好:
Pen, Pencil, Eraser = range(0, 3)
使用 range
还可以设置任何 起始值:
Pen, Pencil, Eraser = range(9, 12)
除了上面所述的之外,如果您还要求这些项目属于某种容器,那么请将它们嵌入到一个类中:
class Stationery:
Pen, Pencil, Eraser = range(0, 3)
要使用枚举项,现在需要使用容器名称和项名称:stype = Stationery.Pen
复杂的枚举:
对于长列表的枚举或更复杂的枚举用法,这些解决方案将不足以满足需求。您可以参考Will Ware在Python Cookbook中发表的关于模拟Python中的枚举的方法。在线版本可在此处获取。
更多信息:
PEP 354: Python中的枚举详细介绍了有关Python中枚举的提议及其被拒绝的原因。
range
函数,当第一个参数为 0 时,可以省略它。 - ToonAlfrinkmy_enum = dict(map(reversed, enumerate(str.split('Item0 Item1 Item2'))))
。然后可以在查找中使用 my_enum
,例如,my_enum['Item0']
可以作为序列的索引。您可能希望将 str.split
的结果包装在一个函数中,如果有任何重复项,则抛出异常。 - Ana NimbusFlag1, Flag2, Flag3 = [2**i for i in range(3)]
。 - majkelx在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论坛上指出有一些枚举库在使用中,包括:
枚举类可以只需要一行代码。
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)]
in
关键字来搜索成员,非常方便。示例用法:'Claimed' in Enum(['Unclaimed', 'Claimed'])
- Farzad Abdolhosseinienum.Enum
相比有什么优势吗?还是这只是一个答案,因为Python 3.6已经被弃用,Python 3.4引入了内置的枚举包? - Martin Thoma所以,我同意。让我们不在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]
enum.Enum
吗?还是这只是一个应该被删除的答案,不是因为Python 3.6已经被弃用,Python 3.4引入了内置的枚举包? - Martin ThomaPython没有内置等同于enum
的功能,其他答案提供了一些自己实现的想法(您可能也对Python cookbook中的过度版本感兴趣)。
然而,在需要在C中调用enum
的情况下,我通常会只使用简单字符串:由于对象/属性的实现方式,(C)Python已经针对短字符串进行了优化,所以使用整数并不会真正带来任何性能优势。为了防止打字错误/无效值,您可以在特定位置插入检查。
ANIMALS = ['cat', 'dog', 'python']
def take_for_a_walk(animal):
assert animal in ANIMALS
...
与使用类相比的一个缺点是失去了自动完成的好处。
2013年5月10日,Guido同意将PEP 435 加入Python 3.4标准库。这意味着Python终于内置支持枚举类型了!
对于Python3.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
了解更多信息,请参考该提案。官方文档很可能很快就会出现。
auto()
。 - fuenfundachtzig