Возможно ли вернуть экземпляр в Python?

1
class Parent():
  def __init__(self):
    self.child = Child()

class Child():
  def __init__(self):
    # get Parent instance
    self.parent = self.Instantiator()

Я знаю, что это не правильная инкапсуляция, а ради интереса...

Учитывая класс "Родительский", который создает объект "Ребенок", возможно ли изнутри Child вернуть объект Parent, который его создал? И если нет, мне любопытно, поддерживают ли какие-либо языки?

Теги:
oop

5 ответов

2
Лучший ответ

Здесь достаточно простое метаклассовое решение проблемы:

import functools

class MetaTrackinits(type):

  being_inited = []

  def __new__(cls, n, b, d):
    clob = type.__new__(cls, n, b, d)

    theinit = getattr(clob, '__init__')
    @functools.wraps(theinit)
    def __init__(self, *a, **k):
      MetaTrackinits.being_inited.append(self)
      try: theinit(self, *a, **k)
      finally: MetaTrackinits.being_inited.pop()
    setattr(clob, '__init__', __init__)

    def Instantiator(self, where=-2):
      return MetaTrackinits.being_inited[where]
    setattr(clob, 'Instantiator', Instantiator)

    return clob

__metaclass__ = MetaTrackinits


class Parent():
  def __init__(self):
    self.child = Child()

class Child():
  def __init__(self):
    self.parent = self.Instantiator()


p = Parent()
print p
print p.child.parent

типичный выход, в зависимости от платформы, будет чем-то вроде

<__main__.Parent object at 0xd0750>
<__main__.Parent object at 0xd0750>

Вы можете получить аналогичный эффект (в версии 2.6 и более поздний) с помощью декоратора класса, но тогда все классы, нуждающиеся в функциональности (как для родительских, так и для детей), должны быть явно оформлены - здесь они просто должны иметь тот же метакласс, который может быть менее навязчивым благодаря "модулю-глобальному __metaclass__ настройке" идиома (и тот факт, что метаклассы, отличные от классных украшений, также наследуются).

На самом деле, это достаточно просто, что я бы подумал о том, чтобы разрешить его в производственном коде, если потребность в этом магическом методе "instantiator" была доказанной бизнес-основой (я бы никогда не позволил, в производственном кодексе, взломать на основе хождения кадры стека! -). (BTW, "разрешающая" часть исходит из передовой практики обязательных обзоров кода: изменения кода не попадают в багажник кодовой базы без консенсуса со стороны рецензентов - это, как работают типовые проекты с открытым исходным кодом, а также как мы всегда работать у моего работодателя).

  • 0
    Это будет забавно, когда кто-то создаст два совершенно независимых экземпляра Parent в разных потоках, а один из экземпляров Child имеет Parent из другого потока или, возможно, даже имеет другого Child в качестве родителя.
  • 0
    @Duncan, если вам нужна поточно-безопасная версия, вы можете просто (например) добавить threading.RLock в MetaTrackinits и a with MetaTrackinits.thatlock: блок вокруг вложенного __init__ - это не ракетостроение. Учитывая текущие ограничения на многопоточность в CPython, мы не используем многопоточность, поэтому общее правило заключается в том, что функциональность по умолчанию не должна быть поточно-безопасной, если это явно не задокументировано.
Показать ещё 15 комментариев
7

Чтобы ответить на вопрос, нет, нет способа 1 дочерний экземпляр знает о каких-либо классах, содержащих ссылки на него. Общий способ 2:

class Parent(object):
    def __init__(self):
        self.child = Child()
        self.child._parent = self

1 Конечно, это не совсем верно. Как отметил еще один комментарий, вы можете извлечь фрейм стека из исполняемого кода в методе __init__ и изучить словарь f_locals для переменной self для кадра до текущего исполняемого. Но это сложно и подвержено ошибкам. Очень не рекомендуется.

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

class Parent(object):
    def __init__(self):
        self.child = Child(self)

class Child(object):
    def __init__(self, parent):
        self._parent = parent
1

Вот пример, основанный на некоторых предложениях Криса Б., чтобы показать, насколько ужасно было бы проверить стек:

import sys

class Child(object):
    def __init__(self):
        # To get the parent:
        #  1. Get our current stack frame
        #  2. Go back one level to our caller (the Parent() constructor).
        #  3. Grab it locals dictionary
        #  4. Fetch the self instance.
        #  5. Assign it to our parent property.
        self.parent = sys._getframe().f_back.f_locals['self']

class Parent(object):
    def __init__(self):
        self.child = Child()

if __name__ == '__main__':
    p = Parent()
    assert(id(p) == id(p.child.parent))

Конечно, это сработает, но просто не пытайтесь реорганизовать его в отдельный метод или создайте из него базовый класс.

0

вы можете * попытаться использовать модуль traceback, чтобы доказать точку.

** Не пробуйте это дома, дети *

0

Это можно сделать в python с метаклассами.

  • 0
    Очень злой еще.
  • 0
    Зависит от ситуации, метаклассы имеют свое применение. Я думаю, что это в основном возникает, когда кто-то хочет сделать динамическое связывание с некомпонентным компонентом. Кстати, базовый «объект» python сделан с метаклассами, если я не ошибаюсь. (У Python есть унаследованный класс 'object' с другим поведением, поэтому мы рекомендуем явно наследовать от объекта).
Показать ещё 1 комментарий

Ещё вопросы

Сообщество Overcoder
Наверх
Меню