I'm having trouble working with an Enum where some attributes have the same value. I think Enums are so new to python that I can't find any other reference to this issue. In any case, let's say I have the following
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
Clearly these are the card numbers and their corresponding values in black jack. The ten through king have the same value. But if I do something like print(CardNumber.QUEEN)
, I get back <CardNumber.TEN: 10>
. What's more, if I iterate over these, it simply iterates over unique values.
>>> 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
How can I get around this issue? I want CardNumber.QUEEN and CardNumber.TEN to be unique, and both appear in any iteration. The only thing I could think of was to give each attribute a second value which would act as a distinct id, but that seems unpythonic.
Yes, labels with duplicate values are turned into aliases for the first such label.
You can enumerate over the __members__
attribute, it is an ordered dictionary with the aliases included:
>>> 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
However, if you must have label-and-value pairs that are unique (and not aliases), then enum.Enum
is the wrong approach here; it doesn't match the usecases for a card game.
In that case it'll be better to use a dictionary (consider using collections.OrderedDict()
if order is important too).
Update
Using aenum
1 you have a couple choices:
use NamedConstant
instead: does not provide any of the Enum
extras (iterating, lookups, etc.) [see: original answer below]
use NoAlias
: has all the normal Enum
behavior except every member is unique and by-value lookups are not available
An example of 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
and in use:
>>> 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
Original Answer
If you want named constants and don't care about the other features of Enum
s, you can use the NamedConstant
class from the aenum library:
from aenum import NamedConstant
class CardNumber(NamedConstant):
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
Duplicate values are still distinct:
>>> CardNumber.TEN is CardNumber.JACK
False
>>> CardNumber.TEN == CardNumber.JACK
True
>>> CardNumber.TEN == 10
True
1 Disclosure: I am the author of the Python stdlib Enum
, the enum34
backport, and the Advanced Enumeration (aenum
) library.
A simple way to do this is to put the value inside an object:
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
By the way, do not use the @dataclass decorator for the Value class. Dataclasses compare the values of its attributes to check if objects are equal, meaning that it would behave exactly like the default Enum!
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With