class Parent():
def __init__(self):
self.child = Child()
class Child():
def __init__(self):
# get Parent instance
self.parent = self.Instantiator()
Я знаю, что это не правильная инкапсуляция, а ради интереса...
Учитывая класс "Родительский", который создает объект "Ребенок", возможно ли изнутри Child вернуть объект Parent, который его создал? И если нет, мне любопытно, поддерживают ли какие-либо языки?
Здесь достаточно простое метаклассовое решение проблемы:
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, "разрешающая" часть исходит из передовой практики обязательных обзоров кода: изменения кода не попадают в багажник кодовой базы без консенсуса со стороны рецензентов - это, как работают типовые проекты с открытым исходным кодом, а также как мы всегда работать у моего работодателя).
Чтобы ответить на вопрос, нет, нет способа 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
Вот пример, основанный на некоторых предложениях Криса Б., чтобы показать, насколько ужасно было бы проверить стек:
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))
Конечно, это сработает, но просто не пытайтесь реорганизовать его в отдельный метод или создайте из него базовый класс.
вы можете * попытаться использовать модуль traceback
, чтобы доказать точку.
** Не пробуйте это дома, дети *
Это можно сделать в python с метаклассами.
threading.RLock
вMetaTrackinits
и awith MetaTrackinits.thatlock:
блок вокруг вложенного__init__
- это не ракетостроение. Учитывая текущие ограничения на многопоточность в CPython, мы не используем многопоточность, поэтому общее правило заключается в том, что функциональность по умолчанию не должна быть поточно-безопасной, если это явно не задокументировано.