Python Enum: How to get enum values with multiple attributes

Question:

I have defined two enums with attributes. When I want to access an enum element by specifying its attribute it works for enum A (one attribute) but not for enum B (two attributes):

from enum import Enum

class A(Enum):
    ValOne = ('One')
    ValTwo = ('Two')

    def __init__(self, num):
        self.num = num

class B(Enum):
    ValOne = ('Val', 'One')
    ValTwo = ('Val', 'Two')

    def __init__(self, val, num):
        self.val = val
        self.num = num

print(A('One'))
print(B('Val', 'One'))

I get the following output:

A.ValOne
Traceback (most recent call last):
  File "test.py", line 19, in <module>
    print(B('Val', 'One'))
  File "/usr/lib/python3.8/enum.py", line 341, in __call__
    return cls._create_(
  File "/usr/lib/python3.8/enum.py", line 444, in _create_
    _, first_enum = cls._get_mixins_(cls, bases)
  File "/usr/lib/python3.8/enum.py", line 576, in _get_mixins_
    raise TypeError("Cannot extend enumerations")
TypeError: Cannot extend enumerations

What am I missing here?

Asked By: Markuz

||

Answers:

The correct syntax is B(('Val', 'One')), passing the value of the enum directly (thus in this case a tuple), or simply naming the enum variant by name: B.ValOne.

I must admit this is confusing, with __init__ automagically destructuring the tuple into two arguments. The error isn’t helpful either.

Answered By: orlp

UPDATE

In Python 3.12 the effective signature for by-value look-up will be:

B(*values)

So

B('Val', 'One')

will work as expected and return

<B.ValOne: ('Val', 'One')>

while

B(('Val', 'One'))

will continue to work correctly.


The biggest thing you missing is that ('One') is not a tuple — you need a comma (,), which would look like ('One', ).

So A is made of single, non-tuple, values 'One' and 'Two'.

B, however, is made of tuples, but

 B('Val', 'One')

is not passing a tuple to B, it is passing two arguments: 'Val' and 'One'. As @orlp mentioned, passing a tuple using function syntax looks like:

B(('Val', 'One'))

Finally, in the definitions of A‘s and B‘s members, you do not need the parentheses:

class A(Enum):
    ValOne = 'One'
    ValTwo = 'Two'

    def __init__(self, num):
        self.num = num


class B(Enum):
    ValOne = 'Val', 'One'
    ValTwo = 'Val', 'Two'

    def __init__(self, val, num):
        self.val = val
        self.num = num

Disclosure: I am the author of the Python stdlib Enum, the enum34 backport, and the Advanced Enumeration (aenum) library.

Answered By: Ethan Furman
Categories: questions Tags: ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.