How to enforce that subclass of abstract class should define a particular inner class in Python

Viewed 117

I have an abstract class from which subclasses will derive. The concrete implementations should include within them an Enum class which holds a set of named constants.

from enum import Enum

class AbstractClass:
    def run(self)
        print('the values are:')
        for enum in ClassEnum:
            print(enum.value)
        self.speak()

    def speak(self):
       raise NotImplementedError
    

class ConcreteClassFirst(AbstractClass):
    class ClassEnum(Enum):
        RED = 0
        GREEN = 1
        BLUE = 2

    def speak(self)
        print('the colors are:')
        for enum in ClassEnum:
            print(enum.name)


class ConcreteClassSecond(AbstractClass):
    class ClassEnum(Enum):
        LION = 'scary'
        ELEPHANT = 'big'
        GIRAFFE = 'tall'

    def speak(self)
        print('the animals are:')
        for enum in ClassEnum:
            print(enum.name)

this code in fact gives the correct behaviour, however I would like there to be some sort of notation (similar to the NotImplementedError on the abstract speak() method) That indicates the author of the concrete class should define an inner Enum class called ClassEnum. It is needed in fact for the run() method.

Some ideas are to have something like

class AbstractClass:
    class ClassEnum(Enum):
        pass

    def run(self):
    ...

but this won't raise an error if the subclass doesn't define its own version of ClassEnum. We could try

class AbstractClass:
    class ClassEnum(Enum):
        raise NotImplementedError

    def run(self):
    ...

But this predictably raises an error as soon as the AbstractClass is defined

I could try

class AbstractClass:
    @property
    def ClassEnum(self):
        raise NotImplementedError

    def run(self):
    ...

But here it's not clear that in the subclass ClassEnum should in fact be a class. Perhpas this approach with some documentation could be appropriate..

1 Answers

I don't think there's a clean way in python short of strange metaclasses to enforce definition of class attributes on subclasses. Given that, I think the next best thing is to instead use instance attributes passed through super.__init__().

from enum import Enum

class AbstractClass:
    def __init__(self, behavior_enum):
        """
        :param behavior_enum: enum class with values and names that determine class behavior
        """
        self.behavior_enum = behavior_enum

    def run(self)
        print('the values are:')
        for enum in self.behavior_enum:
            print(enum.value)
        self.speak()

    def speak(self):
       raise NotImplementedError
    

class ConcreteClassFirst(AbstractClass):
    class behavior_enum(Enum):
        RED = 0
        GREEN = 1
        BLUE = 2

    def __init__(self)
        super().__init__(self.behavior_enum)

    def speak(self)
        print('the colors are:')
        for enum in self.behavior_enum:
            print(enum.name)


class ConcreteClassSecond(AbstractClass):
    class behavior_enum(Enum):
        LION = 'scary'
        ELEPHANT = 'big'
        GIRAFFE = 'tall'

    def __init__(self)
        super().__init__(self.behavior_enum)

    def speak(self)
        print('the animals are:')
        for enum in self.behavior_enum:
            print(enum.name)
Related