我主要是c#开发人员,但我目前正在用Python开发一个项目。
我如何在Python中表示等价的Enum ?
我主要是c#开发人员,但我目前正在用Python开发一个项目。
我如何在Python中表示等价的Enum ?
当前回答
虽然最初的全会提案PEP 354在几年前被否决,但它不断被提出。本打算将某种enum添加到3.2,但它被推回到3.3,然后被遗忘。现在有一个PEP 435打算包含在Python 3.4中。PEP 435的参考实现是flufl.enum。
截至2013年4月,似乎有一个普遍的共识,即应该在3.4的标准库中添加一些东西——只要人们能够就“一些东西”应该是什么达成一致。这是最难的部分。看看从这里和这里开始的线程,以及2013年初的六个其他线程。
与此同时,每次出现这个问题时,PyPI、ActiveState等上都会出现大量的新设计和实现,所以如果你不喜欢FLUFL设计,可以尝试一下PyPI搜索。
其他回答
枚举已按照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
在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
在答案列表中没有看到这个,这是我想出的一个。它允许使用'in'关键字和len()方法:
class EnumTypeError(TypeError):
pass
class Enum(object):
"""
Minics enum type from different languages
Usage:
Letters = Enum(list('abc'))
a = Letters.a
print(a in Letters) # True
print(54 in Letters) # False
"""
def __init__(self, enums):
if isinstance(enums, dict):
self.__dict__.update(enums)
elif isinstance(enums, list) or isinstance(enums, tuple):
self.__dict__.update(**dict((v,k) for k,v in enumerate(enums)))
else:
raise EnumTypeError
def __contains__(self, key):
return key in self.__dict__.values()
def __len__(self):
return len(self.__dict__.values())
if __name__ == '__main__':
print('Using a dictionary to create Enum:')
Letters = Enum(dict((v,k) for k,v in enumerate(list('abcde'))))
a = Letters.a
print('\tIs a in e?', a in Letters)
print('\tIs 54 in e?', 54 in Letters)
print('\tLength of Letters enum:', len(Letters))
print('\nUsing a list to create Enum:')
Letters = Enum(list('abcde'))
a = Letters.a
print('\tIs a in e?', a in Letters)
print('\tIs 54 in e?', 54 in Letters)
print('\tLength of Letters enum:', len(Letters))
try:
# make sure we raise an exception if we pass an invalid arg
Failure = Enum('This is a Failure')
print('Failure')
except EnumTypeError:
print('Success!')
输出:
Using a dictionary to create Enum:
Is a in e? True
Is 54 in e? False
Length of Letters enum: 5
Using a list to create Enum:
Is a in e? True
Is 54 in e? False
Length of Letters enum: 5
Success!
以下是我认为有价值的方法:
允许>和<基于枚举中的顺序进行比较,而不是词法顺序 可以地址项目的名称,属性或索引:x.a, 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
使用下面的方法。
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化。它不是一个真正的Enum,但它完成了这项工作。