Как я могу проверить, является ли класс дочерним по отношению к родителю, созданному фабричным классом?
Рассмотрим следующий тривиальный пример:
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
, и в данном конкретном случае это бесполезно. Я хочу, чтобы у каждого дочернего класса был свой счетчик, я не понимаю, как полезно ссылаться на родительский класс.
Возможно, это полезно (если да, напишите ответ), но поскольку предлагаемый дубликат не имеет отношения к фабричным классам, я думаю, что этот вопрос все еще другой.
На этот вопрос очень сложно ответить, потому что это невозможно сделать с вашей текущей настройкой, и есть так много разных способов заставить его работать... Вы можете использовать метакласс или вы можете сделать Base
наследование от еще одного class, или вы можете переписать Base
так, чтобы он считал экземпляры каждого подкласса отдельно и т. д.
Возможный дубликат Понимание Python super() с методами __init__()
Имейте в виду, что A
и B
действительно нет имеют общий базовый класс; у каждого из них есть база разные, обе из которых имеют одно и то же имя. Есть ли причина постоянно создавать разные, но в остальном идентичные классы?
@chepner Я редактирую свой вопрос, чтобы лучше объяснить ситуацию, надеюсь, теперь все ясно
Ну, я не знаю, как правильно получить доступ к этой информации, но при наборе 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 = ", ")
В двух словах, вы не делаете. Вам нужно будет сохранить ссылку на изготовленный класс для сравнения. Вам лучше изучить
__new__
, чтобы реализовать свой счетчик, а не текущий подход.