我开始使用这段代码片段,根据我的理解,该代码片段本质上是一个类别工厂,可以模仿其他语言的“枚举”类型:
def enum(*sequential, **named):
enums = dict(zip(sequential, range(len(sequential))), **named)
return type('Enum', (), enums)
(我从这里开始:How can I represent an 'Enum' in Python?)
我知道它是如何工作的,它确实如此,但是我想制作动态生成的类,它们是'Enum'类型,可迭代,以便我可以执行以下操作以进行健全性检查:
MyEnum = enum('FOO', 'BAR', 'JIMMY')
def func(my_enum_value): # expects one of the MyEnum values
if not my_enum_value in MyEnum:
raise SomeSortOfException
但是,为了使健全性检查工作,我需要使MyEnum可迭代。我在这里阅读:http://pydanny.blogspot.com/2007/10/required-methods-to-make-class-iterable.html我需要添加 iter len 包含和 getitem 方法为了使它可迭代。我开始走这条路(重写enum代码),但卡住了:
def enum(*sequential, **named):
enums = dict(zip(sequential, range(len(sequential))), **named)
enums['_enums'] = enums.copy() # (so I'd have them in the Class in order to use for the methods I'll be implementing to make it iterable)
Enum = type('Enum', (), enums)
Enum.__iter__ = #Er, how do I do this? Guess I'll ask SO
Enum.__len__ = # etc.
# . . .
return Enum
那么,如何使我生成的Enum类可迭代,以便我可以使用'in'字样?这对我来说不是生死攸关的,但是我通过这种方式学习了大量的Python。
答案 0 :(得分:5)
使用比type
更智能的元类。
class EnumMC(type):
def __contains__(self, val):
return val in self.__dict__
def enum(*sequential, **named):
enums = dict(zip(sequential, range(len(sequential))), **named)
return EnumMC('Enum', (), enums)
MyEnum = enum('FOO', 'BAR', 'JIMMY')
def func(my_enum_value): # expects one of the MyEnum values
if not my_enum_value in MyEnum:
raise ValueError()
func('FOO')
func('QUUX')
您可能希望使用实际属性来存储枚举键,而不是依赖于类字典。
答案 1 :(得分:2)
class Enum(dict): pass
def enum(*sequential, **named):
return Enum(zip(sequential, range(len(sequential))), **named)
这更容易阅读,它支持迭代。
编辑:你还需要做
MyEnum = enum('FOO', 'BAR', 'JIMMY')
然后你可以做
'FOO' in MyEnum
您也可以使用
class Enum(dict):
def __init__(self, *sequential, **named):
self.update(zip(sequential, range(len(sequential))))
self.update(named)
def __getattr__(self, attr):
if attr in self:
return self[attr]
else:
return super(Enum, self).__getattr__(attr)
做同样的事情。
编辑2:另一种选择。
from collections import namedtuple
def enum(*sequential):
return namedtuple('Enum', sequential)(*sequential)
仍然以同样的方式调用它,您可以执行MyEnum.FOO
并支持in
。还编辑了上面的类以支持属性访问。
如果您想要连续的整数值,还可以使用range(len(sequential))
来填充namedtuple
值(而不是键),或者使用**kwargs
然后使用kwargs.keys()
和{{ 1}}如果你想指定任意值。
最后一个解决方案是交叉发布到How can I represent an 'Enum' in Python?
答案 2 :(得分:1)
我不确定为什么enum
需要返回class
而不是instance
,以便您可以轻松实现__contains__
,我也不确定为什么我们甚至需要在python中使用枚举,无论如何,如果你需要在类级别添加__contains__
,你需要一个元类,这里就是
class EnumMeta(type):
def __contains__(self, x):
return x in self.__dict__
class EnumBase(object):
__metaclass__ = EnumMeta
def enum(*sequential, **named):
enums = dict(zip(sequential, range(len(sequential))), **named)
return type('Enum', (EnumBase,), enums)
MyEnum = enum('FOO', 'BAR', 'JIMMY')
print 'foobar in MyEnum','foobar' in MyEnum
print 'FOO in MyEnum', 'FOO' in MyEnum
输出:
foobar in MyEnum False
FOO in MyEnum True