我主要是c#开发人员,但我目前正在用Python开发一个项目。
我如何在Python中表示等价的Enum ?
我主要是c#开发人员,但我目前正在用Python开发一个项目。
我如何在Python中表示等价的Enum ?
当前回答
枚举已按照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枚举,这就是我在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
所有类变量都定义为元组,就像构造函数一样。到目前为止,还不能使用命名参数。
def M_add_class_attribs(attribs):
def foo(name, bases, dict_):
for v, k in attribs:
dict_[k] = v
return type(name, bases, dict_)
return foo
def enum(*names):
class Foo(object):
__metaclass__ = M_add_class_attribs(enumerate(names))
def __setattr__(self, name, value): # this makes it read-only
raise NotImplementedError
return Foo()
像这样使用它:
Animal = enum('DOG', 'CAT')
Animal.DOG # returns 0
Animal.CAT # returns 1
Animal.DOG = 2 # raises NotImplementedError
如果你只想要唯一的符号,不关心值,替换这行:
__metaclass__ = M_add_class_attribs(enumerate(names))
用这个:
__metaclass__ = M_add_class_attribs((object(), name) for name in names)
对于旧的Python 2.x
def enum(*sequential, **named):
enums = dict(zip(sequential, [object() for _ in range(len(sequential))]), **named)
return type('Enum', (), enums)
如果你命名它,是你的问题,但如果不创建对象而不是值允许你这样做:
>>> DOG = enum('BARK', 'WALK', 'SIT')
>>> CAT = enum('MEOW', 'WALK', 'SIT')
>>> DOG.WALK == CAT.WALK
False
当使用这里的其他实现时(在我的例子中使用命名实例时),必须确保永远不要尝试比较来自不同枚举的对象。这里有一个可能的陷阱:
>>> DOG = enum('BARK'=1, 'WALK'=2, 'SIT'=3)
>>> CAT = enum('WALK'=1, 'SIT'=2)
>>> pet1_state = DOG.BARK
>>> pet2_state = CAT.WALK
>>> pet1_state == pet2_state
True
呵!
虽然最初的全会提案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搜索。
有趣的是,前几天我正好需要这个,但我找不到一个值得使用的实现……所以我自己写了:
import functools
class EnumValue(object):
def __init__(self,name,value,type):
self.__value=value
self.__name=name
self.Type=type
def __str__(self):
return self.__name
def __repr__(self):#2.6 only... so change to what ever you need...
return '{cls}({0!r},{1!r},{2})'.format(self.__name,self.__value,self.Type.__name__,cls=type(self).__name__)
def __hash__(self):
return hash(self.__value)
def __nonzero__(self):
return bool(self.__value)
def __cmp__(self,other):
if isinstance(other,EnumValue):
return cmp(self.__value,other.__value)
else:
return cmp(self.__value,other)#hopefully their the same type... but who cares?
def __or__(self,other):
if other is None:
return self
elif type(self) is not type(other):
raise TypeError()
return EnumValue('{0.Name} | {1.Name}'.format(self,other),self.Value|other.Value,self.Type)
def __and__(self,other):
if other is None:
return self
elif type(self) is not type(other):
raise TypeError()
return EnumValue('{0.Name} & {1.Name}'.format(self,other),self.Value&other.Value,self.Type)
def __contains__(self,other):
if self.Value==other.Value:
return True
return bool(self&other)
def __invert__(self):
enumerables=self.Type.__enumerables__
return functools.reduce(EnumValue.__or__,(enum for enum in enumerables.itervalues() if enum not in self))
@property
def Name(self):
return self.__name
@property
def Value(self):
return self.__value
class EnumMeta(type):
@staticmethod
def __addToReverseLookup(rev,value,newKeys,nextIter,force=True):
if value in rev:
forced,items=rev.get(value,(force,()) )
if forced and force: #value was forced, so just append
rev[value]=(True,items+newKeys)
elif not forced:#move it to a new spot
next=nextIter.next()
EnumMeta.__addToReverseLookup(rev,next,items,nextIter,False)
rev[value]=(force,newKeys)
else: #not forcing this value
next = nextIter.next()
EnumMeta.__addToReverseLookup(rev,next,newKeys,nextIter,False)
rev[value]=(force,newKeys)
else:#set it and forget it
rev[value]=(force,newKeys)
return value
def __init__(cls,name,bases,atts):
classVars=vars(cls)
enums = classVars.get('__enumerables__',None)
nextIter = getattr(cls,'__nextitr__',itertools.count)()
reverseLookup={}
values={}
if enums is not None:
#build reverse lookup
for item in enums:
if isinstance(item,(tuple,list)):
items=list(item)
value=items.pop()
EnumMeta.__addToReverseLookup(reverseLookup,value,tuple(map(str,items)),nextIter)
else:
value=nextIter.next()
value=EnumMeta.__addToReverseLookup(reverseLookup,value,(str(item),),nextIter,False)#add it to the reverse lookup, but don't force it to that value
#build values and clean up reverse lookup
for value,fkeys in reverseLookup.iteritems():
f,keys=fkeys
for key in keys:
enum=EnumValue(key,value,cls)
setattr(cls,key,enum)
values[key]=enum
reverseLookup[value]=tuple(val for val in values.itervalues() if val.Value == value)
setattr(cls,'__reverseLookup__',reverseLookup)
setattr(cls,'__enumerables__',values)
setattr(cls,'_Max',max([key for key in reverseLookup] or [0]))
return super(EnumMeta,cls).__init__(name,bases,atts)
def __iter__(cls):
for enum in cls.__enumerables__.itervalues():
yield enum
def GetEnumByName(cls,name):
return cls.__enumerables__.get(name,None)
def GetEnumByValue(cls,value):
return cls.__reverseLookup__.get(value,(None,))[0]
class Enum(object):
__metaclass__=EnumMeta
__enumerables__=None
class FlagEnum(Enum):
@staticmethod
def __nextitr__():
yield 0
for val in itertools.count():
yield 2**val
def enum(name,*args):
return EnumMeta(name,(Enum,),dict(__enumerables__=args))
接受或离开它,它做了我需要它做的:)
像这样使用它:
class Air(FlagEnum):
__enumerables__=('None','Oxygen','Nitrogen','Hydrogen')
class Mammals(Enum):
__enumerables__=('Bat','Whale',('Dog','Puppy',1),'Cat')
Bool = enum('Bool','Yes',('No',0))