在 Python 中将字符串转换为枚举
Posted
技术标签:
【中文标题】在 Python 中将字符串转换为枚举【英文标题】:Convert string to Enum in Python 【发布时间】:2017-05-15 09:56:59 【问题描述】:我想知道将字符串转换(反序列化)为 Python 的 Enum 类的正确方法是什么。似乎getattr(YourEnumType, str)
可以完成这项工作,但我不确定它是否足够安全。
更具体地说,我想将 'debug'
string 转换为 Enum 对象,如下所示:
class BuildType(Enum):
debug = 200
release = 400
【问题讨论】:
【参考方案1】:我的类似 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`
【讨论】:
今天做SignInMethod('EMAIL')
跟那个方法效果一样【参考方案2】:
将您的班级签名更改为:
class BuildType(str, Enum):
【讨论】:
您能补充更多细节吗?那么如何使用该类?【参考方案3】:此功能已内置于 Enum [1]:
>>> 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] 官方文档:Enum programmatic access
【讨论】:
如果输入需要被清理,那么备用值呢?Build.get('illegal', Build.debug)
之类的东西?
@Hetzroni: Enum
没有 .get()
方法,但您可以根据需要添加一个,或者只创建一个基本的 Enum
类并始终从该类继承。
@Hetzroni:根据“请求宽恕,而不是许可”原则,您始终可以将访问权限包含在 try/except KeyError 子句中以返回默认值(正如 Ethan 所提到的,可以选择包装这个在你自己的函数/方法中)。
这里值得注意 - 如果使用它进行序列化/反序列化,请为此序列化 name
属性,因此使用 Build.debug.name
而不是 str(Build.debug)
进行这种查找工作(否则它会尝试在不存在的反序列化端找到Build.debug
)。
@Dragonborn 打电话给Build('debug')
是行不通的。类构造函数必须采用 值,即在本例中为 200
或 400
。如答案所述,要传递 name 您必须使用方括号。【参考方案4】:
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)
或者您需要将字符串转换为已知枚举吗?
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
【讨论】:
我的意思是我想将debug
字符串转换为这样的枚举:python class BuildType(Enum): debug = 200 release = 400
很棒的提示!使用__dict__
和getattr
一样吗?我担心与内部 Python 属性发生名称冲突......
哦...是的,它与getattr
相同。我认为没有名称冲突的原因。您只是不能将关键字设置为类的字段。【参考方案5】:
由于MyEnum['dontexist']
将导致错误KeyError: 'dontexist'
,您可能希望静默失败(例如,返回无)。在这种情况下,您可以使用以下静态方法:
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')
【讨论】:
【参考方案6】:对@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
【讨论】:
有什么方法可以覆盖__getitem__
或其他一些内置方法吗?
从哪方面提高?
如果你在你的函数中使用Class,你最好使用@classmethod而不是@staticmethod【参考方案7】:
我只想通知这在 python 3.6 中不起作用
class MyEnum(Enum):
a = 'aaa'
b = 123
print(MyEnum('aaa'), MyEnum(123))
您必须将数据作为这样的元组提供
MyEnum(('aaa',))
编辑: 事实证明这是错误的。感谢评论者指出我的错误
【讨论】:
使用 Python 3.6.6,我无法重现此行为。我认为您在测试时可能犯了一个错误(我知道我是第一次检查时犯了这个错误)。如果您不小心在每个元素之后放置了一个,
(逗号)(就好像这些元素是一个列表一样),那么它会将每个元素视为一个元组。 (即a = 'aaa',
实际上与a = ('aaa',)
相同)
你是对的,这是我的代码中的不同错误。我不知何故认为你需要在每一行后面加上,
,同时定义以某种方式将值转换为元组的枚举【参考方案8】:
另一种选择(如果您的字符串未将 1-1 映射到您的枚举案例特别有用)是将 staticmethod
添加到您的 Enum
,例如:
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')
【讨论】:
非常相关,如果您发现自己经常这样做:pydantic-docs.helpmanual.io 有什么方法可以覆盖__getitem__
或其他一些内置方法吗?以上是关于在 Python 中将字符串转换为枚举的主要内容,如果未能解决你的问题,请参考以下文章
在 TypeScript 中将枚举结构 value:key 转换为 key:value