Вложенный класс Python должен обращаться к переменной в классе
Я видел несколько "решений" для этого, но решение каждый раз кажется "Не использовать вложенные классы, определять классы снаружи, а затем использовать их в обычном режиме". Мне не нравится этот ответ, потому что он игнорирует основную причину, по которой я выбрал вложенные классы, то есть иметь пул констант (связанных с базовым классом), доступный для всех созданных экземпляров подкласса.
Вот пример кода:
class ParentClass:
constant_pool = []
children = []
def __init__(self, stream):
self.constant_pool = ConstantPool(stream)
child_count = stream.read_ui16()
for i in range(0, child_count):
children.append(ChildClass(stream))
class ChildClass:
name = None
def __init__(self, stream):
idx = stream.read_ui16()
self.name = constant_pool[idx]
Все классы передаются по одному параметру, который является обычным классом битового потока. Мое намерение состоит в том, чтобы иметь решение, которое не требует, чтобы я прочитал значение idx для ChildClass, но все еще в ParentClass. Все чтения потока детского класса должны выполняться в дочернем классе.
Этот пример упрощен. Постоянный пул - это не единственная переменная, которая мне нужна для всех подклассов. Переменная idx - это не единственное, что читается в считывателе потока.
Возможно ли это в python? Нет ли способа доступа к родительской информации?
Ответы
Ответ 1
Несмотря на мой "покровительственный" комментарий (честная игра, чтобы назвать это так!), на самом деле есть способы добиться того, чего вы хотите: другого пути наследования. Пара:
-
Напишите декоратор, который исследует класс сразу после его объявления, находит внутренние классы и копирует в него атрибуты из внешнего класса.
-
Сделайте то же самое с метаклассом.
Здесь подход декоратора, поскольку он наиболее прост:
def matryoshka(cls):
# get types of classes
class classtypes:
pass
classtypes = (type, type(classtypes))
# get names of all public names in outer class
directory = [n for n in dir(cls) if not n.startswith("_")]
# get names of all non-callable attributes of outer class
attributes = [n for n in directory if not callable(getattr(cls, n))]
# get names of all inner classes
innerclasses = [n for n in directory if isinstance(getattr(cls, n), classtypes)]
# copy attributes from outer to inner classes (don't overwrite)
for c in innerclasses:
c = getattr(cls, c)
for a in attributes:
if not hasattr(c, a):
setattr(c, a, getattr(cls, a))
return cls
Вот простой пример его использования:
@matryoshka
class outer(object):
answer = 42
class inner(object):
def __call__(self):
print self.answer
outer.inner()() # 42
Однако я не могу не думать, что некоторые из идей, предложенных в других ответах, послужат вам лучше.
Ответ 2
Здесь вам не нужны два класса. Здесь ваш примерный код написан более сжатым способом.
class ChildClass:
def __init__(self, stream):
idx = stream.read_ui16()
self.name = self.constant_pool[idx]
def makeChildren(stream):
ChildClass.constant_pool = ConstantPool(stream)
return [ChildClass(stream) for i in range(stream.read_ui16())]
Добро пожаловать на Python. Классы изменяются во время выполнения. Наслаждайтесь.
Ответ 3
Вы можете получить доступ к родительскому классу через его имя:
class ChildClass:
name = None
def __init__(self, stream):
idx = stream.read_ui16()
self.name = ParentClass.constant_pool[idx]
И снова я не уверен, что понимаю, чего вы пытаетесь достичь.
Ответ 4
Еще один альтернативный вариант:
Когда вы пытаетесь использовать классы в качестве пространств имен, может возникнуть смысл вкладывать внутренние классы в собственный модуль и определять атрибуты глобальных переменных внешнего класса. Другими словами, если вы никогда не собираетесь создавать экземпляр ParentClass
, то он просто служит прославленным модулем.
Глобальные переменные получают плохой рэп в большинстве языков программирования, но они не являются действительно глобальными в Python и хорошо инкапсулированы в модуль.
Ответ 5
Хорошо, следующие работы (еще более упрощенные из вашего примера). Обратите внимание, что вам не нужно "объявлять" переменные-члены на уровне класса, такие как С++/С#/Java и т.д., Просто установите их на self
внутри __init__
:
class ParentClass:
def __init__(self):
self.constant_pool = ["test"]
self.ChildClass.constant_pool = self.constant_pool
self.children = [self.ChildClass()]
class ChildClass:
def __init__(self):
self.name = self.constant_pool[0]
print "child name is", self.name
p = ParentClass() # Prints "child name is test"
Обратите внимание, что вы все равно можете делать то же самое, не вставляя дочерние классы.
Ответ 6
В вашем вопросе используется подкласс слов, поэтому я использую это для интерпретации вашего вопроса. Как и с другими, кто ответил, я не уверен, что понимаю, что вы ищете.
class ParentClass(object):
constant_pool = [c1, c2, c3]
def __init__(self):
# anything not included in your question
class ChildClass(ParentClass):
def __init__(self, stream):
ParentClass.__init__(self)
self.name = ParentClass.constant_pool[stream.read_ui16()]
stream = get_new_stream()
children = []
for count in range(stream.read_ui16()):
children.append(ChildClass(stream))
Этот код использует наследование для вывода ChildClass из ParentClass (и всех методов и т.д.). Constant_pool - это атрибут самого ParentClass, хотя его можно рассматривать как атрибут любого экземпляра ParentClass или ChildClass (говоря self.constant_pool
внутри ChildClass.__init__
будет эквивалентно приведенному выше, но, на мой взгляд, вводит в заблуждение).
Вложение определений классов не требуется. Вложение определения ChildClass в ParentClass означает, что ChildClass является атрибутом ParentClass, не более того. Это не приводит к тому, что ChildClass наследует что-либо от ParentClass.