我主要是c#开发人员,但我目前正在用Python开发一个项目。
我如何在Python中表示等价的Enum ?
我主要是c#开发人员,但我目前正在用Python开发一个项目。
我如何在Python中表示等价的Enum ?
当前回答
下面是亚历克·托马斯解决方案的一个变种:
def enum(*args, **kwargs):
return type('Enum', (), dict((y, x) for x, y in enumerate(args), **kwargs))
x = enum('POOH', 'TIGGER', 'EEYORE', 'ROO', 'PIGLET', 'RABBIT', 'OWL')
assert x.POOH == 0
assert x.TIGGER == 1
其他回答
所以,我同意。让我们不要在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]
我使用元类来实现枚举(在我的想法中,它是一个const)。代码如下:
class ConstMeta(type):
'''
Metaclass for some class that store constants
'''
def __init__(cls, name, bases, dct):
'''
init class instance
'''
def static_attrs():
'''
@rtype: (static_attrs, static_val_set)
@return: Static attributes in dict format and static value set
'''
import types
attrs = {}
val_set = set()
#Maybe more
filter_names = set(['__doc__', '__init__', '__metaclass__', '__module__', '__main__'])
for key, value in dct.iteritems():
if type(value) != types.FunctionType and key not in filter_names:
if len(value) != 2:
raise NotImplementedError('not support for values that is not 2 elements!')
#Check value[0] duplication.
if value[0] not in val_set:
val_set.add(value[0])
else:
raise KeyError("%s 's key: %s is duplicated!" % (dict([(key, value)]), value[0]))
attrs[key] = value
return attrs, val_set
attrs, val_set = static_attrs()
#Set STATIC_ATTRS to class instance so that can reuse
setattr(cls, 'STATIC_ATTRS', attrs)
setattr(cls, 'static_val_set', val_set)
super(ConstMeta, cls).__init__(name, bases, dct)
def __getattribute__(cls, name):
'''
Rewrite the special function so as to get correct attribute value
'''
static_attrs = object.__getattribute__(cls, 'STATIC_ATTRS')
if name in static_attrs:
return static_attrs[name][0]
return object.__getattribute__(cls, name)
def static_values(cls):
'''
Put values in static attribute into a list, use the function to validate value.
@return: Set of values
'''
return cls.static_val_set
def __getitem__(cls, key):
'''
Rewrite to make syntax SomeConstClass[key] works, and return desc string of related static value.
@return: Desc string of related static value
'''
for k, v in cls.STATIC_ATTRS.iteritems():
if v[0] == key:
return v[1]
raise KeyError('Key: %s does not exists in %s !' % (str(key), repr(cls)))
class Const(object):
'''
Base class for constant class.
@usage:
Definition: (must inherit from Const class!
>>> class SomeConst(Const):
>>> STATUS_NAME_1 = (1, 'desc for the status1')
>>> STATUS_NAME_2 = (2, 'desc for the status2')
Invoke(base upper SomeConst class):
1) SomeConst.STATUS_NAME_1 returns 1
2) SomeConst[1] returns 'desc for the status1'
3) SomeConst.STATIC_ATTRS returns {'STATUS_NAME_1': (1, 'desc for the status1'), 'STATUS_NAME_2': (2, 'desc for the status2')}
4) SomeConst.static_values() returns set([1, 2])
Attention:
SomeCosnt's value 1, 2 can not be duplicated!
If WrongConst is like this, it will raise KeyError:
class WrongConst(Const):
STATUS_NAME_1 = (1, 'desc for the status1')
STATUS_NAME_2 = (1, 'desc for the status2')
'''
__metaclass__ = ConstMeta
##################################################################
#Const Base Class ends
##################################################################
def main():
class STATUS(Const):
ERROR = (-3, '??')
OK = (0, '??')
print STATUS.ERROR
print STATUS.static_values()
print STATUS.STATIC_ATTRS
#Usage sample:
user_input = 1
#Validate input:
print user_input in STATUS.static_values()
#Template render like:
print '<select>'
for key, value in STATUS.STATIC_ATTRS.items():
print '<option value="%s">%s</option>' % (value[0], value[1])
print '</select>'
if __name__ == '__main__':
main()
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)
虽然最初的全会提案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搜索。
为什么枚举必须是整数?不幸的是,在不改变Python语言的情况下,我想不出任何好看的构造来生成它,所以我将使用字符串:
class Enumerator(object):
def __init__(self, name):
self.name = name
def __eq__(self, other):
if self.name == other:
return True
return self is other
def __ne__(self, other):
if self.name != other:
return False
return self is other
def __repr__(self):
return 'Enumerator({0})'.format(self.name)
def __str__(self):
return self.name
class Enum(object):
def __init__(self, *enumerators):
for e in enumerators:
setattr(self, e, Enumerator(e))
def __getitem__(self, key):
return getattr(self, key)
也许现在更好的是,为了配置文件或其他远程输入,我们可以自然地对字符串进行测试。
例子:
class Cow(object):
State = Enum(
'standing',
'walking',
'eating',
'mooing',
'sleeping',
'dead',
'dying'
)
state = State.standing
In [1]: from enum import Enum
In [2]: c = Cow()
In [3]: c2 = Cow()
In [4]: c.state, c2.state
Out[4]: (Enumerator(standing), Enumerator(standing))
In [5]: c.state == c2.state
Out[5]: True
In [6]: c.State.mooing
Out[6]: Enumerator(mooing)
In [7]: c.State['mooing']
Out[7]: Enumerator(mooing)
In [8]: c.state = Cow.State.dead
In [9]: c.state == c2.state
Out[9]: False
In [10]: c.state == Cow.State.dead
Out[10]: True
In [11]: c.state == 'dead'
Out[11]: True
In [12]: c.state == Cow.State['dead']
Out[11]: True