Squeak.ru - шаблоны программирования

Вложенный класс 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, но будет ли работать константа_пула как глобальная переменная? 18.06.2011
  • Вызов их ParentClass и ChildClass не делает их родительскими и дочерними классами. Внутренний класс не имеет отношения наследования к охватывающему его классу. Единственный способ сделать то, что вы хотите, - это ParentClass проанализировать себя и дать ссылку на себя каждому из своих дочерних элементов. 18.06.2011
  • Вызов их ParentClass и ChildClass не делает их родительскими и дочерними классами. немного снисходительно и предполагает, что я назвал их таким образом не только для того, чтобы люди читали код и пытались понять, что я делаю. Нужна ли была эта линия? Вы действительно не поняли, почему я назвал их именно так? 18.06.2011
  • Я думаю, что он пытался сказать, что вложенные классы не имеют отношения родитель/потомок, независимо от намерений программиста. Имена, которые вы выбрали, предполагают, что такие отношения возможны, и это то, что он хотел исправить. 18.06.2011
  • Я согласен с ironchefpython. Вложение ChildClass в ParentClass не меняет поведение ChildClass. Это просто означает, что ваш код должен создавать экземпляры, вызывая ParentClass.ChildClass(stream), а не просто ChildClass(stream). 18.06.2011

Ответы:


1

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

  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

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

17.06.2011
  • Ваше предложение декоратора - это именно то, что я искал. В итоге я получил следующее, которое выполняет то, что я хочу в ситуации, в которой я работаю [я не могу понять, как работает форматирование кода в комментариях, я сдаюсь]: def add_parent_links(cls): cls_init = cls.__init__ def __init__(self, s): for k in dir(self): if str(type(getattr(self,k))).find('classobj') > -1: getattr(self,k).parent = self cls_init(self, s) cls.__init__ = __init__ return cls 18.06.2011
  • Для варианта, а именно для получения базового класса frmo «область класса», см. производный-класс-использование-ba" title="область видимости вложенных классов python определяет атрибут класса в производном классе с использованием ba">stackoverflow.com/questions/28734919/ - без ответа 24.04.2015

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

    17.06.2011
  • Понимание списка тоже? Кажется, ты пытаешься произвести впечатление на этого парня. 18.06.2011
  • Использовать Python без использования понятий — это все равно, что съездить в Канкун и не пойти на пляж. 18.06.2011

  • 3

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

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

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

    17.06.2011

    4

    Другой альтернативный дизайн для рассмотрения:

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

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

    17.06.2011

    5

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

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

    17.06.2011
  • Фактический класс, который я использую, имеет 20 подобъектов и 5 «общих» ресурсов, поэтому это решение приведет к 100 строкам назначения, поскольку я применяю каждый общий ресурс к каждому объекту. Это именно тот тип хакерства, которого я пытался избежать, реализуя отношения родитель/потомок. 18.06.2011

  • 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.

    17.06.2011
    Новые материалы

    Угловая структура архитектуры
    Обратите внимание, что эта статья устарела, я решил создать новую с лучшей структурой и с учетом автономных компонентов: https://medium.com/@marekpanti/angular-standalone-architecture-b645edd0d54a..

    «Данные, которые большинство людей используют для обучения своих моделей искусственного интеллекта, поставляются со встроенным…
    Первоначально опубликовано HalkTalks: https://hacktown.com.br/blog/blog/os-dados-que-a-maioria-das-pessoas-usa-para-treinar-seus-modelos-de-inteligencia-artificial- ja-vem-com-um-vies-embutido/..

    Сильный ИИ против слабого ИИ: различия парадигм искусственного интеллекта
    В последние годы изучению и развитию искусственного интеллекта (ИИ) уделяется большое внимание и прогресс. Сильный ИИ и Слабый ИИ — две основные парадигмы в области искусственного интеллекта...

    Правильный способ добавить Firebase в ваш проект React с помощью React Hooks
    React + Firebase - это мощная комбинация для быстрого и безопасного создания приложений, от проверки концепции до массового производства. Раньше (знаете, несколько месяцев назад) добавление..

    Создайте API с помощью Python FastAPI
    Создание API с помощью Python становится очень простым при использовании пакета FastAPI. После установки и импорта вы можете создать приложение FastAPI и указать несколько конечных точек. Каждой..

    Веселье с прокси-сервером JavaScript
    Прокси-серверы JavaScript — это чистый сахар, если вы хотите создать некоторую общую логику в своих приложениях, чтобы облегчить себе жизнь. Вот один пример: Связь клиент-сервер Мы..

    Получить бесплатный хостинг для разработчиков | Разместите свой сайт за несколько шагов 🔥
    Статические веб-сайты — это веб-страницы с фиксированным содержанием и его постоянным содержанием. Но теперь статические сайты также обрабатывают динамические данные с помощью API и запросов...