Velvet Star Monitor

Standout celebrity highlights with iconic style.

general

Convert string to Enum in Python

Writer Olivia Zamora

I wonder what's the correct way of converting (deserializing) a string to a Python's Enum class. Seems like getattr(YourEnumType, str) does the job, but I'm not sure if it's safe enough.

Just to be more specific, I would like to convert a 'debug'string to an Enum object like this:

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

8 Answers

This functionality is already built in to Enum [1]:

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

The member names are case sensitive, so if user-input is being converted you need to make sure case matches:

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

[1] Official docs: Enum programmatic access

8

Another alternative (especially useful if your strings don't map 1-1 to your enum cases) is to add a staticmethod to your Enum, e.g.:

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

Then you can do question_type = QuestionType.from_str('singleSelect')

2

My Java-like solution to the problem. Hope it helps someone...

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`
1
def custom_enum(typename, items_dict): class_definition = """
from enum import Enum
class {}(Enum): {}""".format(typename, '\n '.join(['{} = {}'.format(k, v) for k, v in items_dict.items()])) namespace = dict(__name__='enum_%s' % typename) exec(class_definition, namespace) result = namespace[typename] result._source = class_definition return result
MyEnum = custom_enum('MyEnum', {'a': 123, 'b': 321})
print(MyEnum.a, MyEnum.b)

Or do you need to convert string to known Enum?

class MyEnum(Enum): a = 'aaa' b = 123
print(MyEnum('aaa'), MyEnum(123))

Or:

class BuildType(Enum): debug = 200 release = 400
print(BuildType.__dict__['debug'])
print(eval('BuildType.debug'))
print(type(eval('BuildType.debug')))
print(eval(BuildType.__name__ + '.debug')) # for work with code refactoring
3

An improvement to the answer of @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
4

Since MyEnum['dontexist'] will result in error KeyError: 'dontexist', you might like to fail silently (eg. return None). In such case you can use the following static method:

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

Change your class signature to this:

class BuildType(str, Enum):
1

I just want to notify this does not work in python 3.6

class MyEnum(Enum): a = 'aaa' b = 123
print(MyEnum('aaa'), MyEnum(123))

You will have to give the data as a tuple like this

MyEnum(('aaa',))

EDIT: This turns out to be false. Credits to a commenter for pointing out my mistake

2

Your Answer

Sign up or log in

Sign up using Google Sign up using Facebook Sign up using Email and Password

Post as a guest

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy