我在Python中有一个简单的枚举,如下所示:
from enum import Enum
class MyEnum(Enum):
#All members have increasing non-consecutive integer values.
A = 0
B = 2
C = 10
D = 18
...
我希望给定pred()
成员的函数succ()
和MyEnum
分别返回给定元素之前和之后的MyEnum
成员(就像{{succ(MyEnum.B)
3}})。例如,pred(MyEnum.D)
和MyEnum.C
都应返回succ
。如果在第一个成员上调用pred
的最后一个成员时调用iter(MyEnum)
,则可以引发异常。
似乎没有任何内置方法可以做到这一点,虽然我可以调用Promise
迭代值,但它必须从头开始遍历整个枚举。我本可以实现一个草率循环来实现这个目标,但我知道这个网站上有一些真正的Python大师,所以我问你:有更好的方法吗?
答案 0 :(得分:4)
请注意,您可以在succ
类中提供pred
和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)
用作:
>>> 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>
显然,只有当你实际上有一个简单的方法来计算从一个项目到下一个或前一个项目的值时,这才有效率,但情况可能并非总是如此。
如果您想要以增加一些空间为代价来获得通用的高效解决方案,则可以构建后继函数和前任函数的映射。
你必须在创建类之后将这些作为属性添加(因为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
用作:
>>> 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>}
你可能想要一个自定义异常,而不是KeyError
,但你明白了。
可能有一种方法可以使用元类集成最后一步,但是对于Enum
是使用元类实现的简单事实而言它并不简单,并且编写元类并不容易。
答案 1 :(得分:1)
添加next
和prev
方法(或succ
和pred
)非常简单:
def next(self):
cls = self.__class__
members = list(cls)
index = members.index(self) + 1
if index >= len(members):
# to cycle around
# index = 0
#
# to error out
raise StopIteration('end of enumeration reached')
return members[index]
def prev(self):
cls = self.__class__
members = list(cls)
index = members.index(self) - 1
if index < 0:
# to cycle around
# index = len(members) - 1
#
# to error out
raise StopIteration('beginning of enumeration reached')
return members[index]