Вложенный класс Python должен получить доступ к переменной во включающем классе - PullRequest
11 голосов
/ 18 июня 2011

Я видел несколько «решений» для этого, но каждый раз кажется, что решение «Не используйте вложенные классы, определяйте классы снаружи, а затем используйте их как обычно». Мне не нравится этот ответ, потому что он игнорирует основную причину, по которой я выбрал вложенные классы, а именно наличие пула констант (связанных с базовым классом), доступных для всех создаваемых экземпляров подкласса.

Вот пример кода:

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? Нет ли способа получить доступ к информации родителя?

Ответы [ 6 ]

11 голосов
/ 18 июня 2011

Несмотря на мой «немного покровительственный» комментарий (честная игра, чтобы так его называть!), На самом деле есть способы достичь того, что вы хотите: другой путь наследования. Пара:

  1. Напишите декоратор, который анализирует класс сразу после его объявления, находит внутренние классы и копирует в них атрибуты внешнего класса.

  2. Сделайте то же самое с метаклассом.

Вот подход декоратора, так как он самый простой:

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

Однако я не могу не думать, что некоторые идеи, предложенные в других ответах, будут вам полезнее.

7 голосов
/ 18 июня 2011

Вам не нужно два класса здесь. Вот ваш пример кода, написанный в более сжатой форме.

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. Классы изменчивы во время выполнения. Наслаждайтесь.

2 голосов
/ 18 июня 2011

Вы можете получить доступ к родительскому классу через его имя:

class ChildClass:

    name = None

    def __init__(self, stream):
        idx = stream.read_ui16()
        self.name = ParentClass.constant_pool[idx]

Опять же, я не уверен, что понимаю, чего вы пытаетесь достичь.

1 голос
/ 18 июня 2011

Еще один альтернативный дизайн для рассмотрения:

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

Глобальные переменные получают плохую репутацию в большинстве языков программирования, но они не являются действительно глобальными в Python и хорошо инкапсулированы в модуль.

0 голосов
/ 18 июня 2011

В вашем вопросе используется слово подкласс, поэтому я использую его для интерпретации вашего вопроса.Как и в случае с другими, кто ответил, я не уверен, что понимаю, что вы ищете.

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.

0 голосов
/ 18 июня 2011

Ну, следующие работы (далее упрощенно из вашего примера).Обратите внимание, что вам не нужно «объявлять» переменные-члены на уровне класса, такие как C ++ / C # / 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"

Обратите внимание, что вы все еще можетеделать то же самое без вложенных дочерних классов.

...