如何在Python Enum中优雅地找到下一个和上一个值?

App*_*ish 7 python enums next python-3.x

我在Python中有一个简单的枚举,如下所示:

from enum import Enum

class MyEnum(Enum):
    #All members have increasing non-consecutive integer values.
    A = 0
    B = 2
    C = 10
    D = 18
    ...
Run Code Online (Sandbox Code Playgroud)

我希望函数pred()succ()给定成员MyEnum返回的成员MyEnum分别在给定元素之前和之后(就像Haskell中相同名称的函数一样).例如succ(MyEnum.B),pred(MyEnum.D)两者都应该返回MyEnum.C.如果succ在第一个成员上pred调用最后一个成员,则可以引发异常.

似乎没有任何内置方法来执行此操作,虽然我可以调用iter(MyEnum)迭代值,但它必须从头开始遍历整个枚举.我本可以实现一个草率循环来实现这个目标,但是我知道这个网站上有一些真正的Python大师,所以我问你:有更好的方法吗?

Bak*_*riu 5

请注意,您可以在类内提供succpred方法Enum

class Sequential(Enum):
    A = 1
    B = 2
    C = 4
    D = 8
    E = 16

    def succ(self):
        v = self.value * 2
        if v > 16:
            raise ValueError('Enumeration ended')
        return Sequential(v)

    def pred(self):
        v = self.value // 2
        if v == 0:
            raise ValueError('Enumeration ended')
        return Sequential(v)
Run Code Online (Sandbox Code Playgroud)

用作:

>>> import myenums
>>> myenums.Sequential.B.succ()
<Sequential.C: 4>
>>> myenums.Sequential.B.succ().succ()
<Sequential.D: 8>
>>> myenums.Sequential.B.succ().succ().pred()
<Sequential.C: 4>
Run Code Online (Sandbox Code Playgroud)

显然,只有当您实际上有一种简单的方法来计算从一个项目到下一个或前一个项目的值时(这可能并非总是如此),这才有效。

如果要以增加一些空间为代价的通用有效解决方案,则可以构建后继功能和前任功能的映射。创建类,必须将这些属性添加为属性(因为Enum将属性弄乱了),因此可以使用装饰器来做到这一点:

def add_succ_and_pred_maps(cls):
    succ_map = {}
    pred_map = {}
    cur = None
    nxt = None
    for val in cls.__members__.values():
        if cur is None:
            cur = val
        elif nxt is None:
            nxt = val

        if cur is not None and nxt is not None:
            succ_map[cur] = nxt
            pred_map[nxt] = cur
            cur = nxt
            nxt = None
    cls._succ_map = succ_map
    cls._pred_map = pred_map

    def succ(self):
        return self._succ_map[self]

    def pred(self):
        return self._pred_map[self]

    cls.succ = succ
    cls.pred = pred
    return cls





@add_succ_and_pred_maps
class MyEnum(Enum):
    A = 0
    B = 2
    C = 8
    D = 18
Run Code Online (Sandbox Code Playgroud)

用作:

>>> myenums.MyEnum.A.succ()
<MyEnum.B: 2>
>>> myenums.MyEnum.B.succ()
<MyEnum.C: 8>
>>> myenums.MyEnum.B.succ().pred()
<MyEnum.B: 2>
>>> myenums.MyEnum._succ_map
{<MyEnum.A: 0>: <MyEnum.B: 2>, <MyEnum.C: 8>: <MyEnum.D: 18>, <MyEnum.B: 2>: <MyEnum.C: 8>}
Run Code Online (Sandbox Code Playgroud)

您可能想要一个自定义例外,而不是,KeyError但您明白了。


也许有一种方法可以使用元类来集成最后一步,但是对于简单的事实来说,Enum使用s是通过元类实现的,并且组合元类并不是一件容易的事,这并不简单。