具有重复值的Python枚举
我在使用某些属性具有相同值的枚举时遇到问题。我认为Enum对于python来说是如此之新,以至于我找不到任何其他关于这个问题的参考。无论如何,假设我有以下几点具有重复值的Python枚举,python,enums,Python,Enums,我在使用某些属性具有相同值的枚举时遇到问题。我认为Enum对于python来说是如此之新,以至于我找不到任何其他关于这个问题的参考。无论如何,假设我有以下几点 class CardNumber(Enum): ACE = 11 TWO = 2 THREE = 3 FOUR = 4 FIVE = 5 SIX = 6 SEVEN = 7 EIGHT = 8 NINE
class CardNumber(Enum):
ACE = 11
TWO = 2
THREE = 3
FOUR = 4
FIVE = 5
SIX = 6
SEVEN = 7
EIGHT = 8
NINE = 9
TEN = 10
JACK = 10
QUEEN = 10
KING = 10
很明显,这些是black jack中的卡号及其对应值。十到王的值相同。但是如果我做了一些类似于打印(CardNumber.QUEEN)的事情,我会返回
。更重要的是,如果我迭代这些,它只是迭代唯一的值
>>> for elem in CardNumber:
... print(elem)
CardNumber.ACE
CardNumber.TWO
CardNumber.THREE
CardNumber.FOUR
CardNumber.FIVE
CardNumber.SIX
CardNumber.SEVEN
CardNumber.EIGHT
CardNumber.NINE
CardNumber.TEN
我怎样才能避开这个问题?我希望CardNumber.QUEEN和CardNumber.TEN是唯一的,并且都出现在任何迭代中。我能想到的唯一一件事是给每个属性赋予第二个值,该值将作为一个不同的id,但这似乎不太和谐。是的,具有重复值的标签将转换为第一个此类标签的别名 您可以通过
\uuuu members\uuuu
属性进行枚举,它是一个包含别名的有序字典:
>>> for name, value in CardNumber.__members__.items():
... print(name, value)
...
ACE CardNumber.ACE
TWO CardNumber.TWO
THREE CardNumber.THREE
FOUR CardNumber.FOUR
FIVE CardNumber.FIVE
SIX CardNumber.SIX
SEVEN CardNumber.SEVEN
EIGHT CardNumber.EIGHT
NINE CardNumber.NINE
TEN CardNumber.TEN
JACK CardNumber.TEN
QUEEN CardNumber.TEN
KING CardNumber.TEN
但是,如果必须具有唯一的标签和值对(而不是别名),则enum.enum
是错误的方法;它与纸牌游戏的用例不匹配
在这种情况下,最好使用字典(如果顺序也很重要,可以考虑使用collections.orderedict()
。更新
使用1,您有两个选择:
- 改为使用
:不提供任何NamedConstant
附加功能(迭代、查找等)[请参阅下面的原始答案]Enum
- 使用
:具有所有正常的NoAlias
行为,但每个成员都是唯一的且按值查找不可用Enum
NoAlias
的一个示例:
from aenum import Enum, NoAlias
class CardNumber(Enum):
_order_ = 'EIGHT NINE TEN JACK QUEEN KING ACE' # only needed for Python 2.x
_settings_ = NoAlias
EIGHT = 8
NINE = 9
TEN = 10
JACK = 10
QUEEN = 10
KING = 10
ACE = 11
在使用中:
>>> list(CardNumber)
[<CardNumber.EIGHT: 8>, <CardNumber.NINE: 9>, <CardNumber.TEN: 10>, <CardNumber.JACK: 10>, <CardNumber.QUEEN: 10>, <CardNumber.KING: 10>, <CardNumber.ACE: 11>]
>>> CardNumber.QUEEN == CardNumber.KING
False
>>> CardNumber.QUEEN is CardNumber.KING
False
>>> CardNumber.QUEEN.value == CardNumber.KING.value
True
>>> CardNumber(8)
Traceback (most recent call last):
...
TypeError: NoAlias enumerations cannot be looked up by value
重复值仍然是不同的:
>>> CardNumber.TEN is CardNumber.JACK
False
>>> CardNumber.TEN == CardNumber.JACK
True
>>> CardNumber.TEN == 10
True
1披露:我是、和库的作者。感谢这里的答案,但对于那些使用
enum.auto()
的人,这里有一个关于别名或重复枚举值的扩展答案
import enum
PREDICT_ALIAS = enum.auto()
class Mode(enum.Enum):
FIT = enum.auto()
EVALUATE = enum.auto()
PREDICT = PREDICT_ALIAS
INFER = PREDICT_ALIAS # alias mode
print(Mode.PREDICT is Mode.INFER) # return True :)
对于那些仍然没有考虑使用or的用户,仍然可以进行以下操作:
导入枚举
类别名(enum.enum):
定义初始子类(cls、*kargs、**kwargs):
进口检验
#不幸的是,没有更干净的方法来检索原始成员
对于反向堆栈(inspect.stack()):
frame\u locals=堆栈[0]。f\u locals
enum\u members=frame\u locals.get('enum\u members'))
如果enum_成员为无:
尝试:
enum_members=帧_局部变量['classdict']。_成员\u名称
除了(KeyError,AttributeError):
持续
打破
其他:
引发运行时错误('无法签出AliasedEnum成员!')
#修补程序子类\uuuu getattribute\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu
cls.\u shield\u成员=列表(枚举成员)
cls.\u shield\u getattr=cls.\u getattribute__
def修补程序(自身,钥匙):
如果使用(“U盾”)键启动:
返回对象。获取属性(self,key)
如果键开始使用(“U值”):
如果不是hasattr(自身名称):
自屏蔽计数器=0
elif self.\u shield\u计数器
这取决于一个事实,即标准的enum.EnumMeta.\uuuuu new\uuuuu
重复数据消除使用O(n²)
算法来处理不可损坏的值,应视为不良做法。它仍然可以达到预期的效果:
$python-i别名_enum.py
>>>Fck.A
>>>Fck.B
>>>Fck.C
使用Python3.7.4进行测试,应可在标准库中移植。一种简单的方法是将值放入对象中:
class Value:
def __init__(self, value:Any):
self.value = value
class MyEnum(Enum):
item1 = Value(0)
item2 = Value(0)
item3 = Value(1)
@property
def val(self):
return self.value.value
assert MyEnum.item1.val == 0
assert MyEnum.item2.val == 0
assert MyEnum.item3.val == 1
顺便说一下,不要对值类使用@dataclass装饰器。数据类比较其属性的值以检查对象是否相等,这意味着它的行为与默认枚举完全相同
Enum
显然不是这方面的正确选择。为什么不改为一个OrderedDict
?你和Jornsharpe都认为我应该在这个案例中使用Dicts是正确的。我只是将此作为一个示例,但无论如何,您都帮助我解决了我的问题。您实际上不需要在类定义之外定义predict别名,也不需要添加此间接寻址。您可以设置PREDICT=enum.auto()
并在下一行使用INFER=PREDICT
!
class Value:
def __init__(self, value:Any):
self.value = value
class MyEnum(Enum):
item1 = Value(0)
item2 = Value(0)
item3 = Value(1)
@property
def val(self):
return self.value.value
assert MyEnum.item1.val == 0
assert MyEnum.item2.val == 0
assert MyEnum.item3.val == 1