我主要是c#开发人员,但我目前正在用Python开发一个项目。
我如何在Python中表示等价的Enum ?
我主要是c#开发人员,但我目前正在用Python开发一个项目。
我如何在Python中表示等价的Enum ?
当前回答
我通常使用的解决方案是这个简单的函数来获取动态创建的类的实例。
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
其他回答
为了解码二进制文件格式,我曾经需要一个Enum类。我碰巧想要的特性是简洁的枚举定义,通过整数值或字符串自由创建枚举实例的能力,以及有用的表示。这是我最后得出的结论:
>>> class Enum(int):
... def __new__(cls, value):
... if isinstance(value, str):
... return getattr(cls, value)
... elif isinstance(value, int):
... return cls.__index[value]
... def __str__(self): return self.__name
... def __repr__(self): return "%s.%s" % (type(self).__name__, self.__name)
... class __metaclass__(type):
... def __new__(mcls, name, bases, attrs):
... attrs['__slots__'] = ['_Enum__name']
... cls = type.__new__(mcls, name, bases, attrs)
... cls._Enum__index = _index = {}
... for base in reversed(bases):
... if hasattr(base, '_Enum__index'):
... _index.update(base._Enum__index)
... # create all of the instances of the new class
... for attr in attrs.keys():
... value = attrs[attr]
... if isinstance(value, int):
... evalue = int.__new__(cls, value)
... evalue._Enum__name = attr
... _index[value] = evalue
... setattr(cls, attr, evalue)
... return cls
...
使用它的一个异想天开的例子:
>>> class Citrus(Enum):
... Lemon = 1
... Lime = 2
...
>>> Citrus.Lemon
Citrus.Lemon
>>>
>>> Citrus(1)
Citrus.Lemon
>>> Citrus(5)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 6, in __new__
KeyError: 5
>>> class Fruit(Citrus):
... Apple = 3
... Banana = 4
...
>>> Fruit.Apple
Fruit.Apple
>>> Fruit.Lemon
Citrus.Lemon
>>> Fruit(1)
Citrus.Lemon
>>> Fruit(3)
Fruit.Apple
>>> "%d %s %r" % ((Fruit.Apple,)*3)
'3 Apple Fruit.Apple'
>>> Fruit(1) is Citrus.Lemon
True
主要特点:
str(), int()和repr()都会产生最有用的输出,分别是枚举的名称,它的整数值,以及返回枚举的Python表达式。 构造函数返回的枚举值严格限制为预定义值,没有意外的枚举值。 枚举值是单例的;它们可以与之严格比较
最好的解决方案取决于你需要从你的假枚举中得到什么。
简单的枚举:
如果你只需要枚举作为标识不同项目的名称列表,Mark Harrison(上图)的解决方案是很棒的:
Pen, Pencil, Eraser = range(0, 3)
使用范围还允许你设置任何起始值:
Pen, Pencil, Eraser = range(9, 12)
除此之外,如果你还要求项属于某种类型的容器,那么将它们嵌入到一个类中:
class Stationery:
Pen, Pencil, Eraser = range(0, 3)
要使用枚举项,你现在需要使用容器名和项名:
stype = Stationery.Pen
复杂的枚举:
对于枚举的长列表或更复杂的enum使用,这些解决方案是不够的。你可以参考Will Ware在Python Cookbook中发布的关于在Python中模拟枚举的食谱。这里有一个在线版本。
更多信息:
PEP 354: Python中的枚举有关于Python中的枚举建议的有趣细节,以及为什么它被拒绝。
我喜欢在Python中这样定义枚举:
class Animal:
class Dog: pass
class Cat: pass
x = Animal.Dog
这比使用整数更有漏洞,因为你不必担心确保整数是唯一的(例如,如果你说Dog = 1和Cat = 1,你就完蛋了)。
它比使用字符串更防bug,因为你不必担心拼写错误(例如。 x == "猫"无声失败,但x ==动物。Catt是一个运行时异常)。
附录: 你甚至可以通过让Dog和Cat继承一个具有正确元类的符号类来增强这个解决方案:
class SymbolClass(type):
def __repr__(self): return self.__qualname__
def __str__(self): return self.__name__
class Symbol(metaclass=SymbolClass): pass
class Animal:
class Dog(Symbol): pass
class Cat(Symbol): pass
然后,如果你使用这些值来索引一个字典,请求它的表示将使它们看起来很漂亮:
>>> mydict = {Animal.Dog: 'Wan Wan', Animal.Cat: 'Nyaa'}
>>> mydict
{Animal.Dog: 'Wan Wan', Animal.Cat: 'Nyaa'}
这里是一个实现:
class Enum(set):
def __getattr__(self, name):
if name in self:
return name
raise AttributeError
下面是它的用法:
Animals = Enum(["DOG", "CAT", "HORSE"])
print(Animals.DOG)
这个解决方案是获取定义为列表的枚举类的简单方法(没有更多烦人的整数赋值):
enumeration.py:
import new
def create(class_name, names):
return new.classobj(
class_name, (object,), dict((y, x) for x, y in enumerate(names))
)
example.py:
import enumeration
Colors = enumeration.create('Colors', (
'red',
'orange',
'yellow',
'green',
'blue',
'violet',
))