在Python中将字符串转换为Enum

将字符串转换为Enum子类的对应实例的正确方法是什么?似乎getattr(YourEnumType, str)做的工作,但我不确定它是否足够安全。

举个例子,假设我有一个枚举

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

给定字符串'debug',我如何得到BuildType.debug作为结果?

188995 次浏览
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)

或者你需要将字符串转换为已知的 Enum?

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


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

或者:

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

这个功能已经内置在Enum中:

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

成员名是大小写敏感的,所以如果用户输入被转换,你需要确保大小写匹配:

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

另一种替代方法(特别是当你的字符串没有将1-1映射到你的枚举情况时)是在你的Enum中添加staticmethod,例如:

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

然后你可以执行question_type = QuestionType.from_str('singleSelect')

我只是想通知这在python 3.6中不起作用

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


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

你必须像这样以元组的形式给出数据

MyEnum(('aaa',))
< p >编辑: 事实证明这是错误的。感谢一位指出我错误的评论者

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

我的类似java的解决方案。希望它能帮助到某人…

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`

由于MyEnum['dontexist']将导致错误KeyError: 'dontexist',您可能希望无声地失败(例如。返回None)。在这种情况下,你可以使用下面的静态方法:

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

更改你的类签名为:

class BuildType(str, Enum):
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__))

例子:

print(LogLevel('Info'))
print(LogLevel(logging.WARNING))
print(LogLevel(10))    # logging.DEBUG
print(LogLevel.fatal)
print(LogLevel(550))

输出:

LogLevel.info
LogLevel.warning
LogLevel.debug
LogLevel.critical
ValueError: 550 is not a valid LogLevel