Вложенный класс 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.