Getting container/parent object from within python

Question:

In Python, is it possible to get the object, say Foo, that contains another object, Bar, from within Bar itself? Here is an example of what I mean

class Foo(object):
    def __init__(self):
        self.bar = Bar()
        self.text = "Hello World"

class Bar(object):
    def __init__(self):
        self.newText = foo.text #This is what I want to do, 
                                #access the properties of the container object

foo = Foo()

Is this possible? Thanks!

Answers:

Pass a reference to the Bar object, like so:

class Foo(object):
    def __init__(self):
        self.text = "Hello World"  # has to be created first, so Bar.__init__ can reference it
        self.bar = Bar(self)

class Bar(object):
    def __init__(self, parent):
        self.parent = parent
        self.newText = parent.text

foo = Foo()

Edit: as pointed out by @thomleo, this can cause problems with garbage collection. The suggested solution is laid out at http://eli.thegreenplace.net/2009/06/12/safely-using-destructors-in-python/ and looks like

import weakref

class Foo(object):
    def __init__(self):
        self.text = "Hello World"
        self.bar = Bar(self)

class Bar(object):
    def __init__(self, parent):
        self.parent = weakref.ref(parent)    # <= garbage-collector safe!
        self.newText = parent.text

foo = Foo()
Answered By: Hugh Bothwell

is it possible to get the object, say Foo, that contains another object, Bar, from within Bar itself?

Not “automatically”, because the language isn’t built like that, and in particular, the language is built such that there is no way to guarantee that Foo exists.

That said, you can always do it explicitly. Attributes, like every other identifier in Python, are just names, not storage space for data; so nothing prevents you from letting the Bar instance have a manually assigned foo attribute that is a Foo instance, and vice-versa at the same time.

Answered By: Karl Knechtel

What about using inheritance:

class Bar(object):
    def __init__(self):
        self.newText = self.text

class Foo(Bar):
    def __init__(self):
        self.txt = 'Hello World'
        Bar.__init__(self)

foo = Foo()
print foo.newText
Answered By: voscausa

Yes, it’s possible. Even without passing the container reference on object creation, i.e. if your object is a class attribute.
Your object needs to implement the descriptor protocol (have a __get__()):

class ChildName(SimpleNamespace):                                                         

    def __get__(self, instance, owner):
        # instance is our parent
        return f'I am {self.name}, my parent is {instance.name}.'


class ChildDiff(SimpleNamespace):

    @property
    def diff(self):
        return self.born - self.parent.born

    def age_diff(self):
        return f'I am {self.diff} years older than {self.parent.name}.'

    def __get__(self, instance, owner):
        self.parent = instance  # XXX: weakref?
        return self  # expose object to be able call age_diff() etc.


class Parent(SimpleNamespace):

    child_name = ChildName(name='Bar')
    child_diff = ChildDiff(born=42)


parent = Parent(name='Foo', born=23)
print(parent.child_name)             # ... I am Bar, my parent is Foo.
print(parent.child_diff.age_diff())  # ... I am 19 years older than Foo.
Answered By: Da Mn

This worked for me:

the parent:

import child

obj_1  = 25             # an object that both parent and child can access

def startup():          # any startup function
    global obj_1
    child.ref( obj_1 )  # send the shared object to the child
    ...

the child:

obj_1 = 0               # initual value will be overwritten

def ref(shared_obj):    # receive a reference to the shared object
    global  obj_1       # shared object is global in the child, optional
    obj_1 = shared_obj  # set obj_1 in the child to be obj_1 in the parent
Answered By: Centipede
Categories: questions Tags: , , ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.