python - 如何创建允许同义词的 Python Enum?
问题描述
我正在规范化现有的、混乱的数据,我想创建一个Enum
允许成员规范名称的同义词,这样如果有人在实例化枚举时使用同义词值,他们就会得到规范的值。IE。
class TrainOutcome(enum.Enum):
PASSED = "PASSED"
SUCCESS = "PASSED" # Deprecated synonym for "PASSED"
FAILED = "FAILED"
STARTED = "STARTED"
这执行得很好,但生成的枚举没有按预期运行:
>>> TrainOutcome("PASSED")
<TrainOutcome.PASSED: 'PASSED'>
# I want to get <TrainOutcome.PASSED: 'PASSED'> here as well
>>> TrainOutcome("SUCCESS")
ValueError: 'SUCCESS' is not a valid TrainOutcome
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/usr/lib/python3.8/enum.py", line 309, in __call__
return cls.__new__(cls, value)
File "/usr/lib/python3.8/enum.py", line 600, in __new__
raise exc
File "/usr/lib/python3.8/enum.py", line 584, in __new__
result = cls._missing_(value)
File "/usr/lib/python3.8/enum.py", line 613, in _missing_
raise ValueError("%r is not a valid %s" % (value, cls.__name__))
ValueError: 'SUCCESS' is not a valid TrainOutcome
尽管该__members__
属性似乎完全按照我希望的方式映射事物:
>>> TrainOutcome.__members__
mappingproxy({'PASSED': <TrainOutcome.PASSED: 'PASSED'>, 'SUCCESS': <TrainOutcome.PASSED: 'PASSED'>, 'FAILED': <TrainOutcome.FAILED: 'FAILED'>, 'STARTED': <TrainOutcome.STARTED: 'STARTED'>})
>>> TrainOutcome['SUCCESS']
<TrainOutcome.PASSED: 'PASSED'>
>>> TrainOutcome['PASSED']
<TrainOutcome.PASSED: 'PASSED'>
如何创建枚举,以便构造函数接受并返回与索引类型相同的值?
编辑:现有的具有重复值的 Python 枚举不能回答我的问题,因为本质上它试图实现与我所追求的相反。那里的 OP 想让结果值更加明显,我想让它们不那么明显。实际上,理想的解决方案是根本没有同义词成员(因为我使用的是生成Enum
的 SQLAlchemy 上下文,它查看成员名称,而不是它们的值),并且只是在构建期间默默地替换为,"SUCCESS"
但是在调用的枚举上"PASSED"
定义自定义似乎不起作用。__init__
super()
编辑: 这个问题和答案提供了迄今为止最简单的解决方案:使用aenum.MultiValueEnum
.
否则,这是一个本土解决方案,似乎符合您在 Python 3.6+ 中应该如何做的精神,在某种程度上受到@Green Cloak Guy 的回答的启发:
class EnumSynonymMixin:
"""
Enum mixin which provides the ability to define synonyms,
ie. values which can be passed into an enum's constructor, that
name the same member as one of the defined values, without adding
any extra members (useful for using with SQLAlchemy's Enum mapping)
For example:
class MyEnum(EnumSynonymMixin, enum.Enum):
FOO = "FOO"
BAR = "BAR"
@classmethod
def synonyms(cls):
return {"MYFOO": "FOO"}
>>> MyEnum("MYFOO")
<MyEnum.FOO: 'FOO'>
"""
@classmethod
def synonyms(cls):
"""Override to provide a dictionary of synonyms for values that can be
passed to the constructor"""
return {}
@classmethod
def _missing_(cls, val):
synonyms = cls.synonyms()
if val in synonyms:
return cls.__members__[synonyms[val]]
return super()._missing(val)
class TrainOutcome(EnumSynonymMixin, enum.Enum):
PASSED = "PASSED"
FAILED = "FAILED"
STARTED = "STARTED"
@classmethod
def synonyms(cls):
return {"SUCCESS": "PASSED"}
解决方案
这应该做你想要的。本质上,类包装器的TrainOutcome(value)
行为类似于TrainOutcome[value]
前者会产生错误(如您所描述的情况,您试图用“SUCCESS”调用它)。它通过拦截调用__new__()
并替换第一个参数来做到这一点。
根据对您问题的评论,您可能不应该这样做 - 我几乎没有理由想到为什么TrainOutcome['SUCCESS']
不能满足您的需求。
def callActsLikeGetitem(c):
oldnew = c.__new__
def newwrapper(cls, *args, **kwargs):
try:
return oldnew(cls, *args, **kwargs)
except ValueError:
if len(args) > 0:
args = (cls[args[0]].name, *args[1:])
return oldnew(cls, *args, **kwargs)
c.__new__ = newwrapper
return c
@callActsLikeGetitem
class TrainOutcome(enum.Enum):
PASSED = "PASSED"
SUCCESS = "PASSED" # Deprecated synonym for "PASSED"
FAILED = "FAILED"
STARTED = "STARTED"
TrainOutcome("SUCCESS")
# <TrainOutcome.PASSED: 'PASSED'>
推荐阅读
- arduino - Arduino 将值从 file.read() 转换为 const char*
- json - 如何通过命令行或手动将 json 文件数据加载到现有的 couchbase 存储桶中?
- angular - 如何从详细信息页面Angular 9中的单个对象(本地JSON)获取详细信息
- sql - 存储过程不适用于动态 where 子句
- css - 字体显示在桌面但不在移动设备上
- c++ - C++ Expat 仅打印标签中元素和数据的首字母
- windows-10 - 为什么在社交媒体上从我的网站共享链接时会显示本地 IP 地址?
- css - Ionic React 不能在 IonItem 中使用 flexbox 或加载外部 .css
- r - ggplot2中具有多个标签/信息的饼图
- java - 如何在 Retrofit 中将变量传递给 GET 参数