tags:

views:

161

answers:

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

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

I know this isn't proper encapsulation but for interest's sake...

Given a "Parent" class that instantiates a "Child" object, is it possible from within Child to return the Parent object that instantiated it? And if no, I'm curious, do any languages support this?

+5  A: 

To answer the question, no, there's no way1 the child instance knows about any classes which contain references to it. The common2 way to handle this is:

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

1 Of course, this isn't strictly true. As another commentor noted, you can extract the stack frame from the executing code within the __init__ method, and examine the f_locals dictionary for the self variable for the frame before the currently executing one. But this is complicated, and prone to error. Highly unrecommended.

2 A slightly better way to handle this (depending on the specific needs of the program) might be to require the parent to pass itself to the child, like so:

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

class Child(object):
    def __init__(self, parent):
        self._parent = parent
Chris B.
A: 

you could* try to use the traceback module, just to prove a point.

*Don't try this at home, kids

catchmeifyoutry
A: 

This can be done in python with metaclasses.

Arafangion
Very evil still.
jcao219
Depends on the situation, metaclasses have their uses. I think this mostly arises when one wants to do dynamic binding to some non-python component.Incidentially, python's base "object" is done with metaclasses unless I'm mistaken. (Python has a legacy 'object' class that has different behaviour, which is why we're encouraged to explicitly inherit from object).
Arafangion
Incidentially, I much prefer metaclass hacks to those that use the traceback module.
Arafangion
+1  A: 

Here's an example based off of some of Chris B.'s suggestions to show how absolutely terrible it would be to inspect the stack:

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

Sure that'll work, but just never try to refactor it into a seperate method, or create a base class from it.

sdolan
+2  A: 

Here's a reasonably-simple metaclass solution to the problem:

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

a typical output, depending on the platform, will be something like

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

You could obtain a similar effect (in 2.6 and later) with a class decorator, but then all classes needing the functionality (both parent and children ones) would have to be explicitly decorated -- here, they just need to have the same metaclass, which may be less intrusive thanks to the "module-global __metaclass__ setting" idiom (and the fact that metaclasses, differently from class-decorations, also get inherited).

In fact, this is simple enough that I would consider allowing it in production code, if the need for that magical "instantiator" method had a proven business basis (I would never allow, in production code, a hack based on walking the stack frames!-). (BTW, the "allowing" part comes from the best-practice of mandatory code reviews: code changes don't get into the trunk of the codebase without consensus from reviewers -- this how typical open source projects work, and also how we always operate at my employer).

Alex Martelli
That's going to be fun when someone creates two completely independent Parent instances in different threads and one of the Child instances has the Parent from the other thread or perhaps even has the other Child as its parent.
Duncan
@Duncan, if you need a threadsafe version you can just (for example) add a `threading.RLock` to `MetaTrackinits` and a `with MetaTrackinits.thatlock:` block around the nested `__init__` -- it ain't rocket science. Given CPython's current threading limitations we don't use threading much, so the general rule is that functionality, by default, doesn't need to be threadsafe unless explicitly documented to be so.
Alex Martelli
That's true. My main concern was that in production Parent and Child would look like simple safe classes and would probably be well separated from the metaclass so they would look to all intents as though they were safe. Given that you can write them trivially as simple and safe by just passing the Parent parameter I don't think it's worth the risk.
Duncan
If I saw something like this in production code, I'd fire the programmer responsible. It's too clever by half.
Chris B.
@Chris, sounds like words spoken by somebody unfamiliar with custom metaclasses (which are a perfectly solid technique when reqs make them appropriate, and nowhere near "clever", esp. in the negative connotation this word has in the Python community). Criticizing the requirements themselves is valid (but that should mean a comment and downvote on the _question_, not here;-), but criticizing the best way to _meet_ those requirements (if, as I said, they're a proven business need) is just silly (of course, the downvote only damages _your_ rep, since I max rep out every day anyway;-).
Alex Martelli
@Alex: Since the custom metaclass either 1) Applies for your module alone (where you could do the right thing anyway) or 2) Requires other modules (or classes) to set their metaclass appropriately, it's difficult to see what this gains you over just requiring you pass the parent in the Child constructor. The metaclass is more complicated, more verbose, and harder to debug. I don't see much of an upside.
Chris B.
@Chris, custom metaclasses **are inherited**, as I already said! All you need to say is "to get the Instantiator method, subclass X" and supply class X that has said custom metaclass. What's that nonsense about "set the metaclass appropriately"?! To get a certain method in your class, you need to inherit a given class X -- that's all there is to it, perfectly ordinary OOP stuff! More and more it sounds as if you just don't **get** custom metaclasses, ignoring the most elementary details of their operation, such as their inheritability. (cont.)
Alex Martelli
(cont.) The point of inheriting functionality, of course, quite independently from whether that functionality is implemented at a meta-level or not, is *not* having to code it again, over and over, repetitiously all over the place, no matter how simple it may be -- the implementation (which as it happens must be in a custom metaclass) is in its own module, **ONCE**, so (if business requirements for the functionality are proven) any alleged "verbosity" is totally amortized by having it once, out of the way of application logic, rather than 999 times weaved throughout it. Baby OOP stuff, really!
Alex Martelli
I wasn't talking about the case where you inherit from a Child class, but the case where you want this functionality in a different class (`Parent2`, say, which might only have the fact that it contains a `Child` in common with `Parent`). Besides, if you're inheriting from `Parent`, the parent takes care of the reference to the child, whether you're using metaclasses or passing it as an argument. All this code does is buy you unnecessary complexity.
Chris B.
@Chris, you don't have to inherit from `Parent`, which has its specific pattern of instantiating children, nor `Child`, which has its specific choice of holding the parent reference in an instance attribute with a specific name -- you inherit from `X`, which does no child-instantiation per se, just supports the needed scaffolding for the `Instantiator` method and the method itself. And *that* buys you exactly that functionality and method, free of constraints about what exactly the children's `__init__` do with the parent-reference they can get from it, what exact class the children are, etc.
Alex Martelli
@Alex: There is no `X` defined in the above code.
Chris B.
@Chris, of course not, nor do the child and parent classes do anything useful or perceivable -- it's **skeleton** code in the A just like it was in the Q, focusing on the structural issues. I introduced the term `X` in the comments, after it became clear that you were ranting against custom metaclasses without understanding them.
Alex Martelli
@Alex: so, you're saying, rather than subclass `Parent` or `Child` from the module above, I should subclass `X`, a hypothetical class mentioned in this thread.
Chris B.
@Chris *sigh* you should of course subclass the classes whose functionality you want to reuse and expand (not others). If you want to reuse and expand the exact functionality of `Parent`, or `Child`, then subclassing them is perfectly fine. If you want to reuse and expand the functionality of class `X` instead (just the `Instantiator` method and support for it), then that's the class you should inherit from -- not different ones. Have you _ever_ done any OOP, or taken some `OOP 101` kind of course...?
Alex Martelli
@Alex: The choice is between 1) define a child class that takes a parent as an argument in the constructor or 2) define a metaclass, inherit from it in the parent, and inherit from it in the child. You really think #2 is easier, simpler, and more elegant than #1?
Chris B.
@Chris, not necessarily -- depends on whether it's a proven business requirement to avoid passing the parent, as I've said over and over and over again. IF that bit of the specs is a proven biz req, THEN this is the way to implement it -- it's **not** "clever", nor complex, nor fragile (I've not seen you post ANY alternative to implement that spec -- only rail against that spec, NOT to the party who posed it, the Q, but only to my **IF** the spec is fixed, **THEN** (etc).
Alex Martelli
@Alex: there's a difference between "is it possible" and "is it smart". There's no "proven business requirement" here; the poster wanted to know if the child knows about the class in which it was instantiated. The answer to that question is "Not without effort, and there's simpler ways to accomplish it."
Chris B.