我主要是c#开发人员,但我目前正在用Python开发一个项目。
我如何在Python中表示等价的Enum ?
我主要是c#开发人员,但我目前正在用Python开发一个项目。
我如何在Python中表示等价的Enum ?
当前回答
在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
其他回答
我通常使用的解决方案是这个简单的函数来获取动态创建的类的实例。
def enum(names):
"Create a simple enumeration having similarities to C."
return type('enum', (), dict(map(reversed, enumerate(
names.replace(',', ' ').split())), __slots__=()))()
使用它非常简单,只需使用包含想要引用的名称的字符串调用函数即可。
grade = enum('A B C D F')
state = enum('awake, sleeping, dead')
这些值只是整数,因此如果需要可以利用它(就像在C语言中一样)。
>>> grade.A
0
>>> grade.B
1
>>> grade.F == 4
True
>>> state.dead == 2
True
有趣的是,前几天我正好需要这个,但我找不到一个值得使用的实现……所以我自己写了:
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))
这里是一个实现:
class Enum(set):
def __getattr__(self, name):
if name in self:
return name
raise AttributeError
下面是它的用法:
Animals = Enum(["DOG", "CAT", "HORSE"])
print(Animals.DOG)
对于旧的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
呵!
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)