List all Enum items (including the one from base class) in python

75 views Asked by At

I want to list all enumeration items with a method. The method is defined in the base class as class functions. The base class has subclasses, and the subclasses have additional subclasses. I used the python extension extendable-enum.

I updated the example code:

from enum import Enum
from extendableenum import inheritable_enum
import inspect


@inheritable_enum
class baseClass(Enum):
    BASEELEMENT= 'Baseelement'

    @classmethod
    def list(cls):
        classHierachy = inspect.getmro(cls)
        for singleClass in classHierachy[0:-2]:
            print(singleClass.__name__)  # debug output
            print(list(map(lambda singleClass: singleClass.name, eval(singleClass.__name__))))

    def get_list_with_bases(class1, output=None):
        if output is None:
            output = []
        try:
            names = list(class1)
        except TypeError:
            return output
        for class2 in class1.__bases__:
            get_list_with_bases(class2, output)
        output.extend(names)
        return output


@inheritable_enum
class middleClass(baseClass):
    MIDDLECLASSELEMENT = 'middleClassElement'

class endClass(middleClass, baseClass):
    ENDCLASSELEMENT = 'endClassElement'


print(endClass.BASEELEMENT)
print(endClass.MIDDLECLASSELEMENT)
print(endClass.ENDCLASSELEMENT)
endClass.list()
# endClass.get_list_with_bases(endClass)

Expected output:

baseClass.BASEELEMENT
middleClass.MIDDLECLASSELEMENT
endClass.ENDCLASSELEMENT
endClass
['ENDCLASSELEMENT']
middleClass
MIDDLECLASSELEMENT
baseClass
BASEELEMENT

But I get:

baseClass.BASEELEMENT
middleClass.MIDDLECLASSELEMENT
endClass.ENDCLASSELEMENT
endClass
['ENDCLASSELEMENT']
middleClass
[]
baseClass
[]
3

There are 3 answers

0
Dakolas On

According to the example available in the project repo it looks like it can't be accessed that way

https://github.com/gweesip/extendable-enum/blob/main/docs/examples/example_inheritable_enum.py

# And create a subclass with new members
class MoreFruit(Fruit):
    MANGO = 4
    DRAGONFRUIT = 5

# Only the non-inherited members will be in _member_names_
print(MoreFruit._member_names_)

# Inherited members are not included in dir, len, reversed or iteration
print(dir(MoreFruit))
print(len(MoreFruit))
print([member for member in reversed(MoreFruit)])
print([member for member in MoreFruit])

# Inherited members can not be accessed by name
try:
    MoreFruit['APPLE']
except KeyError:
    print('Access to super members by name not supported')

# or by value
try:
    MoreFruit(1)
except ValueError:
    print('Access by value not allowed')

# but can still be accessed as attributes.
print(MoreFruit.APPLE)
# The returned members are the superclass members.
print(MoreFruit.APPLE is Fruit.APPLE)
2
pts On

Based on the answer by @Dakolas, there is not direct way to list all enum names.

Use .__bases__ as a workaround:

# And create a subclass with new members
class MoreFruit(Fruit):
    MANGO = 4
    DRAGONFRUIT = 5

print(list(MoreFruit) + list(MoreFruit.__bases__[0]))

As a more general solution, write a recursive function to visit all base classes on all levels:

def get_list_with_bases(class1, output=None):
  if output is None:
    output = []
  try:
    names = list(class1)
  except TypeError:
    return output
  for class2 in class1.__bases__:
    get_list_with_bases(class2, output)
  output.extend(names)
  return output

# And create a subclass with new members
class MoreFruit(Fruit):
    MANGO = 4
    DRAGONFRUIT = 5

print(get_list_with_bases(MoreFruit))
1
Grismar On

It generally helps to make code simpler if you're trying to understand a problem with it.

This does something very similar to your code, but greatly simplified:

from enum import Enum
from extendableenum import inheritable_enum


@inheritable_enum
class ExampleA(Enum):
    A = 1


class ExampleAB(ExampleA):
    B = 2


print(ExampleAB.A, ExampleAB.A.value)
print(ExampleAB.B, ExampleAB.B.value)

for x in ExampleAB:
    print('x:', x)

Instead of multiple classes adding things, there's a simple base class ExampleA that only has A in it, with value 1. And the derived class ExampleAB that also has B in it, with value 2.

Instead of the method you had, that could have been written like this as user @Barmar pointed out in the comments:

    @classmethod
    def list(cls):
        return list(map(lambda c: c.name, cls))

I've just added a simple for loop at the end that also iterates over everything in the enum and shows what is inside.

The output:

ExampleA.A 1
ExampleAB.B 2
x: ExampleAB.B

This clearly shows that:

  • ExampleAB does indeed make the values from both classes available.
  • The actual value of ExampleAB.A is still ExampleA.A from the superclass.
  • If you loop over ExampleAB, it only really contains ExampleAB.B, it won't include values from the base class.

However, if you just want this functionality, a dict would be a simpler and better choice of course:

exampleA = {
    'A': 1
}

exampleAB = exampleA | {
    'B': 2
}

print(exampleAB['A'])
print(exampleAB['B'])

for x in exampleAB:
    print('x:', x, 'value:', exampleAB[x])

Output:

1
2
x: A value: 1
x: B value: 2