Clase Python Enum (con cadena de cadenas)

He encontrado una forma sencilla de implementar (piratear) una enumeración en Python:

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

Entonces puedo llamar a esto como tal:

 bob = MyEnum.VAL1 

¡Sexy!

Muy bien, ahora quiero poder obtener tanto el valor numérico si se le da una cadena, o una cadena si se le da un valor numérico. Digamos que quiero que las cadenas coincidan exactamente con las teclas de Enum

Lo mejor que se me ocurre es algo como esto:

     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 

    o algo así (ignorar cómo estoy detectando casos no válidos)

    ¿Hay una manera mejor, más centrada en python, de hacer lo que estoy haciendo arriba? O es lo anterior ya tan conciso como se pone.

    Parece que tiene que haber una mejor manera de hacerlo.

    Bueno, esto es lo que pediste:

     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) 

    Pero realmente no entiendo el punto de Enums en Python. Tiene un sistema de tipos tan rico como generadores y coroutines para administrar los estados.

    Sé que no he usado Enums en Python por más de 12 años, tal vez puedas deshacerte de ellos también 😉

    [El tiempo pasa…]

    El nuevo Python Enum finalmente aterrizó en 3.4, y también ha sido portado hacia atrás . Así que la respuesta a tu pregunta ahora es usar eso. 🙂


    Un ejemplo:

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

    Utilice un dict:

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

    No hay clases necesarias. Los dictados tienen su ritmo de clase porque 1.) son increíblemente eficientes, 2.) tienen un montón de métodos increíbles, y 3.) son una construcción de lenguaje universal. También son extensibles:

     MyEnum['VAL4'] = 4 

    No es aconsejable implementar la funcionalidad de C ++ (u otro idioma) en Python. Si te encuentras “pirateando una enumeración” o algo por el estilo, puedes apostar a la granja que no lo estás haciendo a la manera de Python.

    Si quieres ir por el camino opuesto, construye otro dict. (por ejemplo, {'1':'VAL1', ...}

    Ver: ¿Cómo puedo representar un ‘Enum’ en Python?

    Este es interesante:

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

    Utilizar:

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

    Es simple y ligero. ¿Son alguna desventaja de este enfoque?

    EDIT: AFAIK Las enumeraciones no son un concepto muy pythonic, por eso no se implementaron en primer lugar. Nunca los usé, y no puedo ver ningún caso de uso para ellos en Python. Las enumeraciones son útiles en lenguajes de tipo estático, porque no son dynamics;)

    Esto hará lo que quiera y generalizará su implementación reduciendo ligeramente el código de placa de caldera:

     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 

    Podrías usar diccionarios:

     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 respuestas es definitivamente mejor. Pero dejó el mío como ejemplo de implementación ingenua.

    No debería tener que codificar sus valores dentro de la clase, es mejor que tenga una fábrica de enumeradores. Mientras tanto, solo agregue algunos elementos nicetirs proporcionados por Python, por ejemplo, anule el método de representación o el atributo para obtener:

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

    Ahora solo usa eso:

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

    (Por cierto, la gente en la lista de desarrolladores de Python está hablando de esto, lo más probable es que tengamos una implementación más completa y con suficientes funciones, esto se implementará de forma nativa en Python 3.3)