Reputation: 4817
What's the correct way to convert a string to a corresponding instance of an Enum
subclass? Seems like getattr(YourEnumType, str)
does the job, but I'm not sure if it's safe enough.
As an example, suppose I have an enum like
class BuildType(Enum):
debug = 200
release = 400
Given the string 'debug'
, how can I get BuildType.debug
as a result?
Upvotes: 383
Views: 326256
Reputation: 181
class LogLevel(IntEnum):
critical = logging.CRITICAL
fatal = logging.FATAL
error = logging.ERROR
warning = logging.WARNING
info = logging.INFO
debug = logging.DEBUG
notset = logging.NOTSET
def __str__(self):
return f'{self.__class__.__name__}.{self.name}'
@classmethod
def _missing_(cls, value):
if type(value) is str:
value = value.lower()
if value in dir(cls):
return cls[value]
raise ValueError("%r is not a valid %s" % (value, cls.__name__))
Example:
print(LogLevel('Info'))
print(LogLevel(logging.WARNING))
print(LogLevel(10)) # logging.DEBUG
print(LogLevel.fatal)
print(LogLevel(550))
Output:
LogLevel.info
LogLevel.warning
LogLevel.debug
LogLevel.critical
ValueError: 550 is not a valid LogLevel
Upvotes: 6
Reputation: 6239
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
Upvotes: 3
Reputation: 3462
In Python 3.11 you could also use a StrEnum
. Granted, you would have to replace the values with strings as well, since it keyes on the value, not name (using enum.auto()
for the value is an ok solution).
import enum
class BuildType(enum.StrEnum):
debug = "200"
release = "400"
print(BuildType("debug"))
Upvotes: 6
Reputation: 71
Change your class signature to this:
class BuildType(str, Enum):
Upvotes: 3
Reputation: 3354
If you want to access enum members by name, use item access:
>>> from enum import Enum
>>> class Build(Enum):
... debug = 200
... build = 400
...
>>> Build['debug']
<Build.debug: 200>
It raises KeyError
for unexpected name value.
For following Enum class, where the values are strings:
class BuildType(Enum):
debug = 'debug'
release = 'release'
An enumeration uses call syntax to return members by value:
>>> BuildType('debug')
<BuildType.debug: 'debug'>
It raises ValueError
for unexpected value.
Upvotes: 1
Reputation: 69021
This functionality is already built in to Enum
:
>>> 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()]
Upvotes: 618
Reputation: 1856
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`
Upvotes: 17
Reputation: 1291
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
Upvotes: 17
Reputation: 1045
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')
Upvotes: 2
Reputation: 4799
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')
Upvotes: 50