可以在Python中返回实例化器吗?

时间:2010-06-30 00:06:00

标签: python oop

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

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

我知道这不是适当的封装,但出于利益的考虑......

给定一个实例化“Child”对象的“Parent”类,是否可以从Child中返回实例化它的Parent对象?如果不是,我很好奇,有没有语言支持这个?

5 个答案:

答案 0 :(得分: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 :(得分: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__设置”成语(以及元类与类装饰不同,也可以继承),这可能不那么具有侵入性。

事实上,这很简单,我考虑在生产代码中允许它,如果需要这种神奇的“实例化”方法有一个经过验证的业务基础(我绝不允许,在生产中)代码,一个基于行走堆栈框架的黑客! - )。 (顺便说一句,“允许”部分来自强制性代码审查的最佳实践:如果没有审核者的共识,代码更改不会进入代码库的主干 - 这是典型的开源项目如何工作,以及我们如何始终如一在我的雇主那里经营)。

答案 2 :(得分:1)

这是一个基于Chris B.的一些建议的例子,以显示检查堆栈是多么可怕:

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))

当然可以工作,但绝不会尝试将其重构为单独的方法,或者从中创建基类。

答案 3 :(得分:0)

您可以*尝试使用traceback模块,只是为了证明一点。

**不要在家里试试,孩子们*

答案 4 :(得分:0)

这可以在带有元类的python中完成。