我主要是c#开发人员,但我目前正在用Python开发一个项目。
我如何在Python中表示等价的Enum ?
我主要是c#开发人员,但我目前正在用Python开发一个项目。
我如何在Python中表示等价的Enum ?
当前回答
遵循Aaron Maenpaa提出的类Java枚举实现,我得出了以下结论。我们的想法是使它具有通用性和可解析性。
class Enum:
#'''
#Java like implementation for enums.
#
#Usage:
#class Tool(Enum): name = 'Tool'
#Tool.DRILL = Tool.register('drill')
#Tool.HAMMER = Tool.register('hammer')
#Tool.WRENCH = Tool.register('wrench')
#'''
name = 'Enum' # Enum name
_reg = dict([]) # Enum registered values
@classmethod
def register(cls, value):
#'''
#Registers a new value in this enum.
#
#@param value: New enum value.
#
#@return: New value wrapper instance.
#'''
inst = cls(value)
cls._reg[value] = inst
return inst
@classmethod
def parse(cls, value):
#'''
#Parses a value, returning the enum instance.
#
#@param value: Enum value.
#
#@return: Value corresp instance.
#'''
return cls._reg.get(value)
def __init__(self, value):
#'''
#Constructor (only for internal use).
#'''
self.value = value
def __str__(self):
#'''
#str() overload.
#'''
return self.value
def __repr__(self):
#'''
#repr() overload.
#'''
return "<" + self.name + ": " + self.value + ">"
其他回答
以下是我认为有价值的方法:
允许>和<基于枚举中的顺序进行比较,而不是词法顺序 可以地址项目的名称,属性或索引: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
Alexandru对枚举使用类常量的建议效果很好。
我还喜欢为每组常量添加字典,以查找人类可读的字符串表示。
这有两个目的:a)它提供了一种简单的方法来漂亮地打印你的枚举;b)字典在逻辑上对常量进行分组,以便您可以测试成员关系。
class Animal:
TYPE_DOG = 1
TYPE_CAT = 2
type2str = {
TYPE_DOG: "dog",
TYPE_CAT: "cat"
}
def __init__(self, type_):
assert type_ in self.type2str.keys()
self._type = type_
def __repr__(self):
return "<%s type=%s>" % (
self.__class__.__name__, self.type2str[self._type].upper())
如果你需要数值,这是最快的方法:
dog, cat, rabbit = range(3)
在Python 3中。X你也可以在最后添加一个星号占位符,它将吸收范围内所有剩余的值,以防你不介意浪费内存和无法计数:
dog, cat, rabbit, horse, *_ = range(100)
Python没有内置的enum,其他答案有实现自己的enum的想法(您可能也对Python烹饪书中的顶部版本感兴趣)。
然而,在C中调用枚举的情况下,我通常只使用简单的字符串:由于对象/属性的实现方式,(C)Python已经优化为使用短字符串工作得非常快,因此使用整数并没有任何性能上的好处。为了防止输入错误/无效值,可以在选定的位置插入检查。
ANIMALS = ['cat', 'dog', 'python']
def take_for_a_walk(animal):
assert animal in ANIMALS
...
(与使用类相比,一个缺点是您失去了自动完成的好处)
在答案列表中没有看到这个,这是我想出的一个。它允许使用'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!