Возможно ли вернуть экземпляр в Python? - PullRequest
1 голос
/ 30 июня 2010
class Parent():
  def __init__(self):
    self.child = Child()

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

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

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

Ответы [ 5 ]

7 голосов
/ 30 июня 2010

Чтобы ответить на вопрос, нет, нет никакого способа 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
2 голосов
/ 30 июня 2010

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

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

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

1 голос
/ 30 июня 2010

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

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's 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 голосов
/ 30 июня 2010

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

0 голосов
/ 30 июня 2010

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

** Не пытайтесь делать это дома, дети *

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...