具有重复值的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

我在使用某些属性具有相同值的枚举时遇到问题。我认为Enum对于python来说是如此之新,以至于我找不到任何其他关于这个问题的参考。无论如何,假设我有以下几点

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