Python Enum class (with tostring fromstring)

匿名 (未验证) 提交于 2019-12-03 01:27:01

问题:

I've found a simply way to implement(hack) an enum into Python:

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

I can then call this as such:

bob = MyEnum.VAL1 

Sexy!

Alright, now I want to be able to get both the numerical value if given a string, or a string if given a numerical value. Let's say I want the strings to exactly match up to the Enum key's

The best I could think of is something like this:

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 

or something like that (ignore how i'm catching invalid cases)

Is there a better, more python centric way to do what I'm doing above? Or is the above already as concise as it gets.

It seems like there's got to be a better way to do it.

回答1:

Well, here is what you asked for:

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) 

But I really don't get the point of Enums in Python. It has such a rich type system as well as generators and coroutines to manage states.

I know I've not been using Enums in Python for more than 12 years, maybe you can get rid of them too ;-)



回答2:

[Time passes...]

The new Python Enum has finally landed in 3.4, and has also been backported. So the answer to your question is now to use that. :)


An example:

>>> from enum import Enum >>> class Modes(Enum) : ...    Mode1 = "M1" ...    Mode2 = "M2" ...    Mode3 = "M3" ...  >>> Modes.Mode1   >>> Modes.Mode1.value 'M1'  >>> Modes.Mode1.value 'M1'  >>> Modes['Mode1']    # index/key notation for name lookup   >>> Modes('M1')       # call notation for value lookup   >>> Modes("XXX")      # example error Traceback (most recent call last):   File "", line 1, in    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 


回答3:

Use a dict:

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

No classes necessary. Dicts have your class beat because 1.) they're incredibly efficient, 2.) have a bunch of incredible methods baked in, and 3.) are a universal language construct. They're also extensible:

MyEnum['VAL4'] = 4 

It's not wise to implement C++ (or another language's) functionality in Python. If you find yourself "hacking up an enum" or something of that nature, you can bet the farm you're not doing it the Python way.

If you want to go the opposite way, build another dict. (e.g. {'1':'VAL1', ...}



回答4:

See: How can I represent an 'Enum' in Python?

This one is interesting:

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'] 

Use:

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)' 

It's simple and lightweight. Are they any disadvantages of this approach?

EDIT: AFAIK enums are not very pythonic as a concept, thats why they were not implemented in the first place. I never used them, and can't see any usecase for them in Python. Enums are useful in static typed languages, because they are not dynamic ;)



回答5:

This will do what you want and generalizes your implementation slightly reducing boiler-plate code:

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 


回答6:

You could use dictionaries:

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") 

Edit : THC4k answers is definitely better. But left mine as an example of naive implementation.



回答7:

You should not have to hardcode your values inside the class - you better have an enumerator factory. WHile at that, just add some nicetirs provided by Python, for example, override the represntation method, or attribute getting:

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()) 

Now just use that:

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

(btw, people in the Python developers list are talking about this,most likely we will have a more complete, and with enough features, implementation of this natively by Python 3.3)



标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!