Создание экземпляра класса и совместное использование переменных для абстрактных/статических классов

Простой пример моей проблемы:

class AbstractService:
    subscribers = []

    @classmethod
    def subscribe(cls, service):
        service.subscribers.append(cls)

    @classmethod
    @abstractmethod
    def notify(cls, payload):
        raise NotImplementedError(f"{cls.__name__} not implemented notify()")


class ServiceA(AbstractService):

    @classmethod
    def notify(cls, payload):
        pass


class ServiceB(AbstractService):

    @classmethod
    def notify(cls, payload):
        pass


ServiceB.subscribe(ServiceA)

ServiceA.notify({'message': 'hello'})

Проблема в том, что все службы в этом экземпляре будут иметь общих подписчиков, поэтому я решил создать функцию, которая возвращает абстрактную службу как таковую:


def get_abstract_service():
        
    class AbstractService:
        subscribers = []
    
        @classmethod
        def subscribe(cls, service):
            service.subscribers.append(cls)
    
        @classmethod
        @abstractmethod
        def notify(cls, payload):
            raise NotImplementedError(f"{cls.__name__} not implemented notify()")

    return AbstractService
class ServiceA(get_abstract_service()):

    @classmethod
    def notify(cls, payload):
        pass


class ServiceB(get_abstract_service()):

    @classmethod
    def notify(cls, payload):
        pass


ServiceB.subscribe(ServiceA)

ServiceA.notify({'message': 'hello'})

Теперь это работает, потому что подписчики уникальны для каждого класса, наследующего абстрактный сервис.

Проблемы

  1. Правильно ли это для python или шаблон неверен, если да, то что будет норма в этой ситуации

  2. Как мне обработать ввод для AbstractService, если мое решение правильное (для любой функции, которая требует, чтобы этот тип был передан ей)

Легким решением было бы дать каждому подклассу свою собственную строку subscribers = []. Если вы хотите автоматизировать процесс, дайте базовому классу метод __init_subclass__, который инициализирует список. Ваше «решение» разрушает отношения наследования между классами: каждый подкласс происходит от базового класса совершенно отдельный, и все они имеют одно и то же имя.

jasonharper 17.03.2022 17:51

Спасибо, я думал, что может быть что-то подобное, чего мне не хватало, но я просто не мог его найти, мое решение просто казалось неверным: D.

Octavio del Ser 17.03.2022 17:57
Почему в Python есть оператор "pass"?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Некоторые методы, о которых вы не знали, что они существуют в Python
Некоторые методы, о которых вы не знали, что они существуют в Python
Python - самый известный и самый простой в изучении язык в наши дни. Имея широкий спектр применения в области машинного обучения, Data Science,...
Основы Python Часть I
Основы Python Часть I
Вы когда-нибудь задумывались, почему в программах на Python вы видите приведенный ниже код?
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
Алиса и Боб имеют неориентированный граф из n узлов и трех типов ребер:
Оптимизация кода с помощью тернарного оператора Python
Оптимизация кода с помощью тернарного оператора Python
И последнее, что мы хотели бы показать вам, прежде чем двигаться дальше, это
Советы по эффективной веб-разработке с помощью Python
Советы по эффективной веб-разработке с помощью Python
Как веб-разработчик, Python может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
1
2
18
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

я бы подошел к этому так:

from __future__ import annotations


class AbstractService:
    subscribers: list[AbstractService]

    def __init_subclass__(cls, **kw):
        super().__init_subclass__(**kw)
        cls.subscribers = []

он устанавливает новый экземпляр subscribers для каждого класса после создания.

edit: кроме того, это также обрабатывает ввод текста с помощью from __future__ import annotations

например

class C:
    def f(self) -> C:
        ...

Маленькая рекомендация: тоже звоните super().__init_subclass__(**__). Вы принимаете аргументы ключевых слов, чтобы их можно было передать методу __init_subclass__, который их ожидает.

chepner 17.03.2022 17:53

Спасибо за это, очень полезно.

Octavio del Ser 17.03.2022 17:57

Другие вопросы по теме