如何进行此枚举类型支持迭代?

时间:2014-01-14 17:50:28

标签: python

在Alec Thomas的this answer之后,我使用以下内容创建枚举:

def enum(*sequential):
    enums = dict(zip(sequential, range(len(sequential))))
    return type('Enum', (), enums)

我希望能够获得其中一个枚举的长度。例如,我可以写

>>> Suit = enum('spades', 'hearts', 'diamonds', 'clubs')
>>> Suit.spades
0
>>> Suit.hearts
1
>>> Suit.diamonds
2
>>> Suit.clubs
3

但是没有办法在运行时列出所有枚举值。我希望能够做一些像

这样的事情
>>> [s for s in Suit]
[0, 1, 2, 3]

我尝试在enum['__iter__']函数中分配enum(),但我不知道需要分配哪种对象:

def enum(*sequential):
    enums = dict(zip(sequential, range(len(sequential))))
    enums['__iter__'] = iter(range(len(sequential)))
    return type('Enum', (), enums)

给出

>>> [s for s in Suit]
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: 'type' object is not iterable

如何让enum能够列出其成员? (即使只是enum报告其长度的能力就足够了,因为那时成员只是range(len(Suit))的元素。)

4 个答案:

答案 0 :(得分:7)

问题是type(…)返回类型,即实际用于创建对象的内容。当然,你可以争论这是否是一个问题 - 而且可能不是因为Python的打字系统如何工作(一切都是对象,类型只是type对象的对象等。)。

然而,效果是您无法添加特殊方法,例如在这种情况下需要的__iter__。然而,在对象的类型上查找特殊方法,因此在type上(这是您正在创建的类型的基本类型)。 type - 正如您所期望的那样 - 不可迭代。

因此,如果你想要一些可迭代的东西,你需要创建一个非类型的东西。你可能会在这里提出一些奇特的元类,但是你能做的最简单的事情,也就是你的代码保持相同的长度,实际上是named tuple。这是顺便说一句。另外,Python 3.4 enum type中的语义也受到启发。

>>> def enum(*keys):
        return namedtuple('Enum', keys)(*range(len(keys)))
>>> Suit = enum('spades', 'hearts', 'diamonds', 'clubs')
>>> Suit.spades
0
>>> Suit.hearts
1
>>> Suit.diamonds
2
>>> Suit.clubs
3
>>> [s for s in Suit]
[0, 1, 2, 3]

答案 1 :(得分:4)

我建议您使用collections.namedtuple

使用enumerator的其他实现方式

<强>实施

>>> from collections import namedtuple
>>> def enum(*sequential):
    enums = namedtuple('enums',sequential)(*range(len(sequential)))
    return enums

<强>用法

>>> Suit = enum('spades', 'hearts', 'diamonds', 'clubs')
>>> Suit.spades
0
>>> Suit.hearts
1
>>> Suit.diamonds
2
>>> Suit.clubs
3
>>> [s for s in Suit]
[0, 1, 2, 3]

答案 2 :(得分:3)

这似乎符合您的要求:

def enum(*sequential):
    length = len(sequential)
    pairs = zip(sequential, xrange(len(sequential)))
    Enum = type('Enum', (), dict(pairs))
    Enum.__len__ = lambda _: length
    Enum.__iter__ = lambda _: iter(range(length))
    return Enum()

Suit = enum('spades', 'hearts', 'diamonds', 'clubs')

print Suit.spades              # 0
print Suit.hearts              # 1
print Suit.diamonds            # 2
print Suit.clubs               # 3
print 'len(Suit):', len(Suit)  # len(Suit): 4
print [s for s in Suit]        # [0, 1, 2, 3]

答案 3 :(得分:1)

你的变体有效!在()之后添加type(...)以创建类实例并修改__iter__

def enum(*sequential):
    enums = dict(zip(sequential, range(len(sequential))))
    enums['__iter__'] = lambda e,i=iter(range(len(sequential))): i
    return type('Enum', (object,), enums)()

>>> suit = enum('spades', 'hearts', 'diamonds', 'clubs')
>>> [s for s in suit]
[0, 1, 2, 3]