2010-12-17 91 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