Convertir cadena a Enum en Python

5 minutos de lectura

avatar de usuario
Vladius

¿Cuál es la forma correcta de convertir una cadena en una instancia correspondiente de un Enum subclase? Parece getattr(YourEnumType, str) hace el trabajo, pero no estoy seguro de si es lo suficientemente seguro.

Como ejemplo, supongamos que tengo una enumeración como

class BuildType(Enum):
    debug = 200
    release = 400

Dada la cadena 'debug'como puedo conseguir BuildType.debug ¿como resultado?

  • Contra la versión original de la pregunta, esto es no una tarea de “serialización”.

    – Karl Knechtel

    3 de julio a las 20:49

avatar de usuario
ethan furman

Esta funcionalidad ya está integrada en Enum:

>>> from enum import Enum
>>> class Build(Enum):
...   debug = 200
...   build = 400
... 
>>> Build['debug']
<Build.debug: 200>

Los nombres de los miembros distinguen entre mayúsculas y minúsculas, por lo que si se convierte la entrada del usuario, debe asegurarse de que coincidan las mayúsculas y minúsculas:

an_enum = input('Which type of build?')
build_type = Build[an_enum.lower()]

  • ¿Qué pasa con un valor de reserva en caso de que la entrada deba ser desinfectada? Algo en el tipo de Build.get('illegal', Build.debug)?

    – Hetzroni

    5 de febrero de 2018 a las 10:19

  • @Hetzroni: Enum no viene con un .get() método, pero puede agregar uno según sea necesario, o simplemente hacer una base Enum class y siempre heredar de eso.

    –Ethan Furman

    5 de febrero de 2018 a las 16:29

  • @Hetzroni: según el principio de “pedir perdón, no permiso”, siempre puede envolver el acceso en una cláusula try/except KeyError para devolver el valor predeterminado (y como mencionó Ethan, opcionalmente envuelva esto en su propia función/método) .

    – Laogeodritt

    9 de febrero de 2018 a las 22:37

  • Vale la pena señalar aquí: si usa esto para la serialización / deserialización, serialice el name propiedad para esto, así que usa Build.debug.name más bien que str(Build.debug) para que este tipo de búsqueda funcione (de lo contrario, intenta encontrar Build.debug en el lado de la deserialización que no existirá).

    – fquinner

    9 de octubre de 2019 a las 11:22

  • @Dragonborn No funcionaría llamar Build('debug'). El constructor de la clase debe tomar el valores decir 200 o 400 en este ejemplo. para pasar el nombre debe usar corchetes, como ya dice la respuesta.

    – Arthur Taca

    20 de enero de 2020 a las 14:34

Otra alternativa (especialmente útil si sus cadenas no se asignan 1-1 a sus casos de enumeración) es agregar un staticmethod para usted Enump.ej:

class QuestionType(enum.Enum):
    MULTI_SELECT = "multi"
    SINGLE_SELECT = "single"

    @staticmethod
    def from_str(label):
        if label in ('single', 'singleSelect'):
            return QuestionType.SINGLE_SELECT
        elif label in ('multi', 'multiSelect'):
            return QuestionType.MULTI_SELECT
        else:
            raise NotImplementedError

Entonces puedes hacer question_type = QuestionType.from_str('singleSelect')

  • Muy relacionado, si te encuentras haciendo esto a menudo: pydantic-docs.helpmanual.io

    – m_flor

    26 de mayo de 2019 a las 1:29

  • ¿Hay alguna forma de sobrescribir el __getitem__ o algún otro método integrado?

    – Clemente Walter

    4 de noviembre de 2020 a las 17:10

avatar de usuario
mitch

Mi solución similar a Java para el problema. Espero que ayude a alguien…

from enum import Enum, auto


class SignInMethod(Enum):
    EMAIL = auto(),
    GOOGLE = auto()

    @classmethod
    def value_of(cls, value):
        for k, v in cls.__members__.items():
            if k == value:
                return v
        else:
            raise ValueError(f"'{cls.__name__}' enum not found for '{value}'")


sim = SignInMethod.value_of('EMAIL')
assert sim == SignInMethod.EMAIL
assert sim.name == 'EMAIL'
assert isinstance(sim, SignInMethod)
# SignInMethod.value_of("invalid sign-in method")  # should raise `ValueError`

  • hoy, hazlo SignInMethod('EMAIL') tuvo el mismo efecto que ese método

    –Rafael Gomes Francisco

    20 de agosto de 2021 a las 18:19

Una mejora a la respuesta de @rogueleaderr:

class QuestionType(enum.Enum):
    MULTI_SELECT = "multi"
    SINGLE_SELECT = "single"

    @classmethod
    def from_str(cls, label):
        if label in ('single', 'singleSelect'):
            return cls.SINGLE_SELECT
        elif label in ('multi', 'multiSelect'):
            return cls.MULTI_SELECT
        else:
            raise NotImplementedError

  • Quiero decir que me gustaría convertir un debug cadena a una enumeración de tales: python class BuildType(Enum): debug = 200 release = 400

    – Vladio

    31 de diciembre de 2016 a las 11:29


  • ¡Grandes consejos! Esta usando __dict__ lo mismo que getattr? Me preocupan las colisiones de nombres con los atributos internos de Python….

    – Vladio

    31 de diciembre de 2016 a las 12:38

  • Oh… sí, es lo mismo que getattr. No veo ninguna razón para las colisiones de nombres. Simplemente no puede establecer una palabra clave como campo de clase.

    – ADR

    31 de diciembre de 2016 a las 13:17

Ya que MyEnum['dontexist'] dará como resultado un error KeyError: 'dontexist', es posible que desee fallar en silencio (por ejemplo, devolver Ninguno). En tal caso, puede utilizar el siguiente método estático:

class Statuses(enum.Enum):
    Unassigned = 1
    Assigned = 2

    @staticmethod
    def from_str(text):
        statuses = [status for status in dir(
            Statuses) if not status.startswith('_')]
        if text in statuses:
            return getattr(Statuses, text)
        return None


Statuses.from_str('Unassigned')

avatar de usuario
Robson

Cambie su firma de clase a esto:

class BuildType(str, Enum):

Solo quiero notificar que esto no funciona en python 3.6

class MyEnum(Enum):
    a="aaa"
    b = 123

print(MyEnum('aaa'), MyEnum(123))

Tendrás que dar los datos como una tupla como esta

MyEnum(('aaa',))

EDITAR: Esto resulta ser falso. Créditos a un comentarista por señalar mi error.

  • Usando Python 3.6.6, no pude reproducir este comportamiento. Creo que puede haber cometido un error durante la prueba (sé que lo hice la primera vez al verificar esto). Si accidentalmente coloca un , (coma) después de cada elemento (como si los elementos fueran una lista), entonces trata cada elemento como una tupla. (es decir a = 'aaa', en realidad es lo mismo que a = ('aaa',))

    – Multicazador

    24 de octubre de 2018 a las 4:06


  • Tienes razón, era un error diferente en mi código. De alguna manera pensé que necesitabas poner , detrás de cada línea al definir la enumeración que convirtió los valores en tuplas de alguna manera

    – Sstuber

    24 de octubre de 2018 a las 10:34


¿Ha sido útil esta solución?

Esta web utiliza cookies propias y de terceros para su correcto funcionamiento y para fines analíticos y para mostrarte publicidad relacionada con sus preferencias en base a un perfil elaborado a partir de tus hábitos de navegación. Al hacer clic en el botón Aceptar, acepta el uso de estas tecnologías y el procesamiento de tus datos para estos propósitos. Configurar y más información
Privacidad