2014-01-14 65 views
2

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()函數內分配enum['__iter__'],但我不知道我需要分配什麼樣的對象:

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列出其成員的能力嗎? (甚至只是來報告它的長度就足夠了,自那時以來,成員的range(len(Suit))簡單的元素能力的枚舉。)

+1

@JoelCornett你不能在'__iter__'補丁,看到我的回答。 – poke

+0

@poke:>。<當然。然而,你可以實例化和迭代。就像'list(Suit())'一樣。假如你已經修補了'__iter__',那就是。 –

回答

7

問題是type(…)返回類型,即實際用於創建對象的東西。現在當然可以爭論是否存在問題 - 這可能不是由於Python的輸入系統的工作原理(一切都是對象,類型只是對象的對象等)。

但是,其效果是,您不能添加特殊方法,如__iter__這種情況下將需要。然而,特殊的方法在對象的類型上查找,所以在這種情況下,在type(這是您創建的類型的基本類型)。如你所料,type不是可迭代的。

所以,如果你想有可迭代的東西,你需要創建一個不是類型的東西。你可能會在這裏想出一些花哨的元類,但是你可以做的最簡單的事情,它也保持你的代碼長度相同,真的是named tuple。這是btw。 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] 
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] 
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] 
2

這似乎滿足您的要求:

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]