Наследование фабричного класса Python

Как я могу проверить, является ли класс дочерним по отношению к родителю, созданному фабричным классом?

Рассмотрим следующий тривиальный пример:

from itertools import count
def basefactory():
    class Base:
        _idc = count(0)
        def __init__(self):
            self._id = next(self._idc)
    return Base

Затем я создаю детей, используя класс factory:

class A(basefactory()):
    def __init__(self):
        super().__init__()

class B(basefactory()):
    def __init__(self):
        super().__init__()

Как я могу проверить, являются ли A или B дочерними элементами класса, созданного фабричным классом?
Без фабричного класса я бы использовал: issubclass(A, Base)) но, конечно, это не работает с фабричным классом, так как Base не определен в глобальном пространстве.

Причина этой настройки в том, что я хочу, чтобы A и B имели разные счетчики _idc для генерации последовательных _id для каждого экземпляра дочерних классов.
Если A и B наследуются напрямую от Base, счетчик является общим. Поясню на примере, что я имею в виду.

С описанной выше настройкой фабричного класса, если я сделаю:

ll = [A(), A(), B(), B()]   
for e in ll:
    print(e._id, end = ", ")

печатает: 0, 1, 0, 1, . Если вместо этого я перемещаю Base в глобальном пространстве, а A и B наследуются непосредственно от Base, предыдущий код печатает: 0, 1, 2, 3, .

Я хочу, чтобы _id было 0, 1, 0, 1, , поэтому я использую заводскую настройку класса. В моем реальном случае у меня нет только A и B (у меня есть 10 классов, созданных фабричным классом, и они могут увеличиться в будущем), поэтому я не хочу повторяться и инициализировать счетчики в каждом потомке.

Однако мне также нужно утверждать, является ли класс дочерним по отношению к Base, или, другими словами, создан ли он через фабричный класс. И это вопрос. Можно ли это сделать, проверив иерархию классов с помощью issubclass? Или мне нужно изменить настройку или использовать обходной путь, как люди предложили в комментариях?

О предлагаемом дубликате

Я не думаю, что это дубликат Понимание Python super() с методами __init__(). Я знаю, что делает super, и в данном конкретном случае это бесполезно. Я хочу, чтобы у каждого дочернего класса был свой счетчик, я не понимаю, как полезно ссылаться на родительский класс. Возможно, это полезно (если да, напишите ответ), но поскольку предлагаемый дубликат не имеет отношения к фабричным классам, я думаю, что этот вопрос все еще другой.

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

deceze 30.05.2019 12:19

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

Aran-Fey 30.05.2019 12:22

Возможный дубликат Понимание Python super() с методами __init__()

Song 06.07.2019 15:33

Имейте в виду, что A и B действительно нет имеют общий базовый класс; у каждого из них есть база разные, обе из которых имеют одно и то же имя. Есть ли причина постоянно создавать разные, но в остальном идентичные классы?

chepner 06.07.2019 16:16

@chepner Я редактирую свой вопрос, чтобы лучше объяснить ситуацию, надеюсь, теперь все ясно

Valentino 06.07.2019 17:16
Почему в 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 может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
2
5
564
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ну, я не знаю, как правильно получить доступ к этой информации, но при наборе help(A) вы получаете Base:

Help on class A in module __main__:
class A(Base)
 |  Method resolution order:
 |      A
 |      Base
 |      builtins.object
 |  
 |  Methods inherited from Base:
 |  
 |  __init__(self)
 |  
 |  ----------------------------------------------------------------------
 |  Data descriptors inherited from Base:
 |  
 |  __dict__
 |      dictionary for instance variables (if defined)
 |  
 |  __weakref__
 |      list of weak references to the object (if defined)

Я понимаю, что должен быть доступ к нему должным образом

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

Я думаю, что декоратор сделает то, что вы хотите (добавит счетчик экземпляров к классу), проще, чем фабричная функция, которая генерирует ненужный в противном случае базовый класс.

from itertools import count

def add_counter(cls):
    cls._idc = count(0)
    old_init = cls.__init__
    def __init__(self, *args, **kwargs):
        old_init(self, *args, **kwargs)
        self._id  = next(self._idc)
    cls.__init__ = __init__
    return cls

@add_counter
class A:
    pass

@add_counter
class B:
    pass

ll = [A(), A(), B(), B()]
for e in ll:
    print(e._id, end = ", ")

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