2010-12-17 90 views
8

我已經找到了一種簡單的方法來實現(黑客)一個枚舉成Python:Python的枚舉類(的toString fromstring)

class MyEnum: 
    VAL1, VAL2, VAL3 = range(3) 

然後我就可以把這個作爲這樣的:

bob = MyEnum.VAL1 

性感!

好吧,現在我希望能夠得到數字值,如果給定一個字符串,或者一個字符串,如果給定一個數值。比方說,我想字符串完全匹配到Enum項的

我能想到的最好的是這樣的:

class MyEnum: 
    VAL1, VAL2, VAL3 = range(3) 
    @classmethod 
    def tostring(cls, val): 
    if (val == cls.VAL1): 
     return "VAL1" 
    elif (val == cls.VAL2): 
     return "VAL2" 
    elif (val == cls.VAL3): 
     return "VAL3" 
    else: 
     return None 
    @classmethod 
    def fromstring(cls, str): 
    if (str.upper() == "VAL1"): 
     return cls.VAL1 
    elif (str.upper() == "VAL2"): 
     return cls.VAL2 
    elif (str.upper() == "VAL2"): 
     return cls.VAL2 
    else: 
     return None 

或類似的東西(忽略如何我追趕無效的情況下)

有沒有更好的,更蟒蛇爲中心的方式來做我上面做的事情?或者上面已經儘可能簡潔。

看起來好像有一個更好的方法來做到這一點。

+4

的的if/else總是表示這是一個錯誤的解決這一數額;) – 2010-12-17 19:49:24

+0

的可能重複[什麼是Python中實現一個「枚舉」最好的方法是什麼? ](http://stackoverflow.com/questions/36932/whats-the-best-way-to-implement-an-enum-in-python) – 2010-12-17 20:10:33

+0

@Nick:你可能想改變你接受的答案。 – 2017-04-05 19:08:08

回答

10

嗯,這裏是你問的:

class MyEnum: 
    VAL1, VAL2, VAL3 = range(3) 
    @classmethod 
    def tostring(cls, val): 
    for k,v in vars(cls).iteritems(): 
     if v==val: 
      return k 

    @classmethod 
    def fromstring(cls, str): 
     return getattr(cls, str.upper(), None) 

print MyEnum.fromstring('Val1') 
print MyEnum.tostring(2) 

但我真的不明白枚舉的點在Python。它有如此豐富的類型系統以及管理狀態的發生器和協程。

我知道我沒有使用枚舉的Python爲超過12年,也許你可以擺脫他們太;-)

+0

MyEnum.tostring(3)返回無。我應該是MyEnum.tostring(MyEnum.VAL3) – Rod 2010-12-17 17:22:17

+0

這是因爲'範圍'從0開始。'tostring(2)'給出了'VAL3'。不幸的名字('* [1-9]')。 Afaik,C/C++'enum'也從0開始。 – delnan 2010-12-17 17:25:57

+0

@delnan真的。我從我在我身邊做的測試中感到困惑。可能是一個副作用,使我無法直接在tostring方法中使用int。 – Rod 2010-12-17 17:32:37

1

你可以使用字典:

class MyEnum: 
    VAL1, VAL2, VAL3 = range(3) 
    __toString = { VAL1 : "VAL1", VAL2 : "VAL2", VAL3 : "VAL3" } 

    @classmethod 
    def tostring(cls, val): 
     return cls.__toString.get(val) 

    @classmethod 
    def fromstring(cls, str): 
     i = str.upper() 
     for k,v in cls.__toString.iteritems(): 
      if v == i: 
       return k 
     return None 


print MyEnum.tostring(MyEnum.VAL1) 
print MyEnum.fromstring("VAL1") 

編輯:THC4k答案是肯定更好。但留下我作爲一個天真實施的例子。

+2

領先的雙下劃線(=名稱修飾)是......好,不是邪惡,但我從來沒有看到假設情況下他們有一個小的好處,而不是使用通常的情況下,他們只是打破東西。 – delnan 2010-12-17 17:40:48

+0

@delnan。我會記下這一點。 – Rod 2010-12-17 17:46:22

7

使用的字典:

MyEnum = {'VAL1': 1, 'VAL2':2, 'VAL3':3} 

沒有必要班。由於1)他們的效率非常高,2)有一堆令人難以置信的方法,3)是一種通用語言結構。它們也是可擴展的:

MyEnum['VAL4'] = 4 

在Python中實現C++(或其他語言)的功能並不明智。如果你發現自己「篡改枚舉」或某種性質的東西,那麼你可以打賭你不是用Python的方式來做它。

如果你想以相反的方式,建立另一個字典。 (例如。{'1':'VAL1', ...}

+2

如果我必須構建第二個字典才能以另一種方式進行操作,那麼這違反了任何語言中首選的單點定義值。你如何迴應這個問題? – Stabledog 2012-05-14 14:07:48

+1

擴展性不是枚舉的功能。 – 2015-04-18 15:29:23

3

參見: How can I represent an 'Enum' in Python?

這一個很有趣:

class EnumMeta(type): 
    def __getattr__(self, name): 
    return self.values.index(name) 

    def __setattr__(self, name, value): # this makes it read-only 
    raise NotImplementedError 

    def __str__(self): 
    args = {'name':self.__name__, 'values':', '.join(self.values)} 
    return '{name}({values})'.format(**args) 

    def to_str(self, index): 
    return self.values[index] 

class Animal(object): 
    __metaclass__ = EnumMeta 
    values = ['Horse','Dog','Cat'] 

用途:

In [1]: Animal.to_str(Animal.Dog) 
Out[1]: 'Dog' 
In [2]: Animal.Dog 
Out[2]: 1 
In [3]: str(Animal) 
Out[3]: 'Animal(Horse, Dog, Cat)' 

很簡單,重量輕。這種方法有什麼缺點嗎?

編輯: AFAIK枚舉不是一個pythonic作爲一個概念,這就是爲什麼他們沒有被首先實現。我從來沒有使用過它們,並且在Python中看不到它們的任何用例。枚舉在靜態類型語言中很有用,因爲它們不是動態的;)

0

您不應該在類內硬編碼值 - 您最好有一個枚舉器工廠。 而在那,只是添加被Python提供了一些nicetirs,例如,覆蓋represntation方法或屬性獲得:

class Enumerator(object): 
    def __init__(self, *names): 
     self._values = dict((value, index) for index, value in enumerate (names)) 
    def __getattribute__(self, attr): 
     try: 
      return object.__getattribute__(self,"_values")[attr] 
     except KeyError: 
      return object.__getattribute__(self, attr) 
    def __getitem__(self, item): 
     if isinstance (item, int): 
      return self._values.keys()[self._values.values().index(item)] 
     return self._values[item] 
    def __repr__(self): 
     return repr(self._values.keys()) 

現在只需使用:

>>> enum = Enumerator("val1", "val2", "val3") 
>>> enum 
['val3', 'val2', 'val1'] 
>>> enum.val2 
1 
>>> enum["val1"] 
0 
>>> enum[2] 
'val3' 

(順便說一句,人在Python開發人員名單正在討論這個問題,我們很可能會在 中有更完整的功能,並且具備足夠的功能,可以通過Python 3.3本地實現此功能)

3

這將完成您想要的任務,並將您的實現slig htly降低鍋爐板代碼:

class EnumBase: # base class of all Enums 
    @classmethod 
    def tostring(cls, value): 
     return dict((v,k) for k,v in cls.__dict__.iteritems())[value] 

    @classmethod 
    def fromstring(cls, name): 
     return cls.__dict__[name] 

class MyEnum(EnumBase): VAL1, VAL2, VAL3 = range(3) 

print MyEnum.fromstring('VAL1') 
# 0 
print MyEnum.tostring(1) 
# VAL2 
18

[時光流逝...]

新的Python枚舉終於登陸3.4,has also been backported。所以你的問題的答案是現在使用它。 :)


一個例子:

>>> from enum import Enum 
>>> class Modes(Enum) : 
... Mode1 = "M1" 
... Mode2 = "M2" 
... Mode3 = "M3" 
... 

>>> Modes.Mode1 
<Modes.Mode1: 'M1'> 

>>> Modes.Mode1.value 
'M1' 

>>> Modes.Mode1.value 
'M1' 

>>> Modes['Mode1'] # index/key notation for name lookup 
<Modes.Mode1: 'M1'> 

>>> Modes('M1')  # call notation for value lookup 
<Modes.Mode1: 'M1'> 

>>> Modes("XXX")  # example error 
Traceback (most recent call last): 
    File "<stdin>", line 1, in <module> 
    File "C:\Anaconda3\lib\enum.py", line 291, in __call__ 
    return cls.__new__(cls, value) 
    File "C:\Anaconda3\lib\enum.py", line 533, in __new__ 
    return cls._missing_(value) 
    File "C:\Anaconda3\lib\enum.py", line 546, in _missing_ 
    raise ValueError("%r is not a valid %s" % (value, cls.__name__)) 
ValueError: 'XXX' is not a valid Modes