Python:使动态创建的类(如类本身)可迭代?

时间:2011-08-07 04:21:53

标签: python

我开始使用这段代码片段,根据我的理解,该代码片段本质上是一个类别工厂,可以模仿其他语言的“枚举”类型:

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。

3 个答案:

答案 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