如何在Python中使用字符串值创建可索引和可切片的枚举?

如何在Python中使用字符串值创建可索引和可切片的枚举?,python,python-3.x,Python,Python 3.x,我有一个这样的文件: class Level(Enum): prerequisite_level: Optional["Level"] dependent_level: Optional["Level"] lower_priority_levels: List["Level"] greater_priority_levels: List["Level"] DATA_CHECK = "data check" DESIGN_CHECK = "desi

我有一个这样的文件:

class Level(Enum):
    prerequisite_level: Optional["Level"]
    dependent_level: Optional["Level"]
    lower_priority_levels: List["Level"]
    greater_priority_levels: List["Level"]

    DATA_CHECK = "data check"
    DESIGN_CHECK = "design check"
    ALERT = "alert"
枚举值是按特定顺序排列的,基于这些级别,我需要能够获得上一个、下一个以及所有上一个和下一个级别。我相信我需要能够以数字形式索引这些级别以获得这些值,因此我添加了一个常量以实现这一点:

INCREASING_PRIORITY_LEVELS: List[Level] = list(Level)

for priority_level_index, threshold_level in enumerate(Level):
    if priority_level_index > 0:
        threshold_level.prerequisite_level = Level[priority_level_index - 1]
    else:
        threshold_level.prerequisite_level = None

    if priority_level_index < len(Level) - 1:
        threshold_level.dependent_level = Level[priority_level_index + 1]
    else:
        threshold_level.dependent_level = None

    threshold_level.lower_priority_levels = Level[:priority_level_index]
    threshold_level.greater_priority_levels = Level[priority_level_index + 1:]
增加优先级级别:列表[级别]=列表(级别)
对于优先级级别索引,枚举中的阈值级别(级别):
如果优先级级别索引>0:
阈值\级别。先决条件\级别=级别[优先级\级别\索引-1]
其他:
阈值\级别。先决条件\级别=无
如果优先级等级索引

这很笨重,我想去掉这个常数。我需要实现
\uuuuu getitem\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu,并创建一个子类
Enum
,将前面提到的子类
EnumMeta
作为元类,以便可以根据需要对该新子类
Enum
的任何子类进行索引:

from itertools import islice
from enum import Enum, EnumMeta

class IndexableEnumMeta(EnumMeta):
    def __getitem__(cls, index):
        if isinstance(index, slice):
            return [cls._member_map_[i] for i in islice(cls._member_map_, index.start, index.stop, index.step)]
        if isinstance(index, int):
            return cls._member_map_[next(islice(cls._member_map_, index, index + 1))]
        return cls._member_map_[index]

class IndexableEnum(Enum, metaclass=IndexableEnumMeta):
    pass

class Level(IndexableEnum):
    DATA_CHECK = "data check"
    DESIGN_CHECK = "design check"
    ALERT = "alert"
因此
级别[1:3]
返回:

[<Level.DESIGN_CHECK: 'design check'>, <Level.ALERT: 'alert'>]
Level.DESIGN_CHECK

(值得赞扬的是@EthanFurman指出了子类化
EnumMeta
的可行性)

在使用方面实现相同结果的一个可能的替代方法是使用
集合。取而代之的是namedtuple

from collections import namedtuple
LevelSequence = namedtuple('Level', ('DATA_CHECK', 'DESIGN_CHECK', 'ALERT'))
Level = LevelSequence('data check', 'design check', 'alert')
以便:

  • Level.DESIGN\u CHECK
    Level[1]
    都返回
    “DESIGN CHECK”
    ,并且
  • 级别[1:3]
    返回
    (“设计检查”、“警报”)
我很难理解上面的内容:。。。[评论澄清前四个应该是属性,
prequisite
dependent
分别是前面和后面的成员]

解决方案是在初始化当前成员时修改以前的成员(诀窍是直到成员创建和初始化之后,当前成员才添加到父级
Enum
)。下面是使用stdlib的
Enum
1(Python 3.6及更高版本)的解决方案:

在使用中:

>>> list(Level)
[<Level.DATA_CHECK: 'data check'>, <Level.DESIGN_CHECK: 'design check'>, <Level.ALERT: 'alert'>]

>>> Level.DATA_CHECK.prerequisite
None

>>> Level.DATA_CHECK.dependent
<Level.DESIGN_CHECK: 'design check'>

>>> Level.DESIGN_CHECK.prerequisite
<Level.DATA_CHECK: 'data check'>

>>> Level.DESIGN_CHECK.dependent
<Level.ALERT: 'alert'>

>>> Level.ALERT.prerequisite
<Level.DESIGN_CHECK: 'design check'>

>>> Level.ALERT.dependent
None
然后你会看到:

>>> Level.DESIGN_CHECK
<Level.DESIGN_CHECK>
>Level.DESIGN\u检查

1如果使用Python 3.5或更早版本,则需要使用2


2披露:我是、和库的作者。

是的,因为它覆盖了生成
Enum
类实例的元类的
\uuuu getitem\uuuuuuuu
。我用一个解决方案更新了我的答案,只覆盖了一个特定类的
\uu getitem\uuuuuu
。非常棒的工作。我个人不会用我的工作环境来换取这么多的复杂性,但是其他人可能会这样做。如果目标是使面向用户的子类
级别
尽可能干净,那么创建修改副本
EnumMeta
Enum
类的复杂性可以隐藏到单独的模块中。正如您从上述示例中注意到的,使用子类
EnumMeta
创建新的
Enum
类MyEnum(Enum,metaclass=MyNewMetaclass)一样简单。
级别值在ORM中用作字段值,并从API返回,因此它们不应该是整数。当存在(具有)数据检查级别时,您只能创建(具有)设计检查级别的(实例),因此
level.design\u check.prerequisite==level.data\u check和level.data\u check.dependent==level.design\u check
@l0b0:Ah,好的。更新了我的答案以更好地匹配您的问题。@l0b0:Woops--将
,name
添加到
\uuuu init\uuuuuu
中@l0b0:做了一点修改--更新了
\u generate\u next\u value\uuuuu
并删除了
\uuu new\uuuuu
方法。这可能要求太多了,但是为什么
\u生成下一个值\u
没有
self
作为第一个参数?这打破了第8页的空白:“方法的N805第一个参数应命名为‘self’”。而且,
mypy
似乎认为自己是
self.\uuuuu class\uuuuu.\uu成员\uu map\uuuu
具有类型
级别
>>> list(Level)
[<Level.DATA_CHECK: 'data check'>, <Level.DESIGN_CHECK: 'design check'>, <Level.ALERT: 'alert'>]

>>> Level.DATA_CHECK.prerequisite
None

>>> Level.DATA_CHECK.dependent
<Level.DESIGN_CHECK: 'design check'>

>>> Level.DESIGN_CHECK.prerequisite
<Level.DATA_CHECK: 'data check'>

>>> Level.DESIGN_CHECK.dependent
<Level.ALERT: 'alert'>

>>> Level.ALERT.prerequisite
<Level.DESIGN_CHECK: 'design check'>

>>> Level.ALERT.dependent
None
    def __repr__(self):
        return '<%s.%s>' % (self.__class__.__name__, self.name)
>>> Level.DESIGN_CHECK
<Level.DESIGN_CHECK>