Как обеспечить, чтобы подкласс абстрактного класса определял определенный внутренний класс в Python

#python #abstract-class #inner-classes

#python #абстрактный класс #внутренние классы

Вопрос:

У меня есть абстрактный класс, из которого будут происходить подклассы. Конкретные реализации должны включать в себя класс Enum, который содержит набор именованных констант.

 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)
  

этот код фактически обеспечивает правильное поведение, однако я хотел бы, чтобы там была какая-то нотация (аналогичная методу NotImplementedError on the abstract speak() ) Это указывает на то, что автор конкретного класса должен определить вызываемый внутренний Enum класс ClassEnum . На самом деле это необходимо для run() метода.

Некоторые идеи должны иметь что-то вроде

 class AbstractClass:
    class ClassEnum(Enum):
        pass

    def run(self):
    ...
  

но это не вызовет ошибку, если подкласс не определяет свою собственную версию ClassEnum . Мы могли бы попробовать

 class AbstractClass:
    class ClassEnum(Enum):
        raise NotImplementedError

    def run(self):
    ...
  

Но это предсказуемо вызывает ошибку, как только AbstractClass определено

Я мог бы попробовать

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

    def run(self):
    ...
  

Но здесь неясно, что в подклассе ClassEnum на самом деле должен быть классом. Возможно, этот подход с некоторой документацией может быть уместным..

Ответ №1:

Я не думаю, что в python есть чистый способ, кроме странных метаклассов, для принудительного определения атрибутов класса в подклассах. Учитывая это, я думаю, что следующая лучшая вещь — вместо этого использовать переданные атрибуты экземпляра 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)