__parent__ - like attribute?

Bengt Richter bokr at oz.net
Mon Jan 20 04:24:27 CET 2003


On Sun, 19 Jan 2003 08:15:45 -0800, Dylan Reinhardt <python at dylanreinhardt.com> wrote:

>At 03:41 AM 1/19/2003, Bengt Richter wrote:
>>Why not write minimal code to illustrate?
>
>
>My first post had this minimal code example:
>
Ok, sorry for my comment.
>
>-------------------------
>
>class spam:
>     __init__(self):
>         self.stuff = eggs()
>         self.changed = 0
>
>class eggs:
>     def do_stuff(self):
>         # after we've done the main part of our work,
>         # signal parent object that information has changed
>         self.__some_namespace_voodoo__.changed = 1
>         return something_unrelated
>
>a = spam()
>b = spam()
>x = a.stuff.do_stuff()   #  <--- should set value of a.changed to 1
>x = b.stuff.do_stuff()   #  <--- should set value of b.changed to 1
>
>-------------------------
>
>
>>several_instances_of_spam[an_index_less_than_several].get_contained_egg(
>>     index_less_than_thousands).changed_attr = some_change
>
>
>The whole point of doing it the way I'm doing it is to provide a clean, 
>Pythonic interface, thus:
>
>spam_instance.egg_instance.egg_method()
>
>should have a side effect on the spam instance.
>
I presume you mean "should" as a requirement of your problem, not
"should because a spam has a reference in it to an egg (BTW, uppercasing
the class name's first letter makes it so we can write Spam and spam
for short instead of spam and spam_instance ;-)

If you made Egg inherit from Spam, there would be a natural "should"
implemented in the attribute access machinery's search sequence. E.g.,
since stuff is an attribute of Spam, you could monitor its use and
though it's not going to be near as fast as direct references, it might
be interesting to file for alternative ideas ... well, easier to show
than talk about ;-)

====< spameggs.py >========================================
class Spam(object):
    stuff_monitoring_list = []
    def _set_changed(self, v): self._changed = v
    def _get_changed(self):
        if self.__class__ is Spam:
            for egg in self.stuff_monitoring_list:
                self._changed = self._changed or egg.changed
            self.stuff_monitoring_list = []
        return self._changed
    changed = property(_get_changed, _set_changed)
    def _set_stuff(self, v): self._stuff = v
    def _get_stuff(self):
        self.stuff_monitoring_list.append(self._stuff)
        return self._stuff
    stuff = property(_get_stuff, _set_stuff)
    def __init__(self):
        self.stuff = Eggs()
        self.changed = 0

class Eggs(Spam):
    def __init__(self): pass # must define or we get recursion via self.stuff above
    def do_stuff(self):
        # after we've done the main part of our work,
        # signal parent object that information has changed
        ## self.__some_namespace_voodoo__.changed = 1
        self.changed = 1 # should find property in parent class
        return 'something_unrelated' ## string, to avoid name error here

if __name__ == '__main__':
    a = Spam()
    b = Spam()
    print 'a=%s a.changed=%s' % (a, a.changed)
    print 'b=%s b.changed=%s' % (b, b.changed)
    
    print 'a.stuff_monitoring_list = %s' % a.stuff_monitoring_list
    x = a.stuff.do_stuff()   #  <--- should set value of a.changed to 1
    print 'a.stuff_monitoring_list = %s' % a.stuff_monitoring_list
    print 'a._changed = %s, a._stuff._changed = %s' % (a._changed, a._stuff._changed)
    print 'a.changed = %s' % a.changed
    print 'a.stuff_monitoring_list = %s' % a.stuff_monitoring_list
    print 'a._changed = %s, a._stuff._changed = %s' % (a._changed, a._stuff._changed)
    print 'b.stuff_monitoring_list = %s' % b.stuff_monitoring_list
    x = b.stuff.do_stuff()   #  <--- should set value of b.changed to 1
    print 'b.stuff_monitoring_list = %s' % b.stuff_monitoring_list
    print 'b.changed = %s' % b.changed
    print 'b.stuff_monitoring_list = %s' % b.stuff_monitoring_list
===========================================================

If you run it, you get:

[19:14] C:\pywk\clp>spameggs.py
a=<__main__.Spam object at 0x007E0F70> a.changed=0
b=<__main__.Spam object at 0x007E0570> b.changed=0
a.stuff_monitoring_list = []
a.stuff_monitoring_list = [<__main__.Eggs object at 0x007E0F40>]
a._changed = 0, a._stuff._changed = 1
a.changed = 1
a.stuff_monitoring_list = []
a._changed = 1, a._stuff._changed = 1
b.stuff_monitoring_list = []
b.stuff_monitoring_list = [<__main__.Eggs object at 0x007E0540>]
b.changed = 1
b.stuff_monitoring_list = []


>>What is your concept of "subobject"?
>
>Hopefully this should be clear from the code example.
>
>>Sure, there's any number of ways to do it. But to choose one, we need to know
>>how well/badly the above code reflects your problem world.
>
>Does the provided code do it?
>
>>    class Egg:
>>         def __init__(self, my_container):
>>             self.my_container = my_container
>
>
>This is actually what I'm doing now.  If this is the best available answer, 
>I can certainly live with that, but it been surprised at what some of the 
>namespace wizards here have come up with in the past, so I thought I'd see 
>if there is a more elegant/efficient answer.
>
I doubt if you could get more efficient than a direct reference, but the property
stuff might be interesting. Note that the Spam and Egg ._changed attributes attach
to Spam and Egg instances respectively, but .changed goes through the property whether
inherited or not -- but the accessor methods see different self instances. An egg
instance still doesn't know about its "parent" container instance, but the Egg class
does Know about the base Spam class, and an instance of either can find a Spam class
variable like the stuff_monitoring_list.

Of course, a reference in that list keeps an egg instance alive, so if that is a
consideration, you might want to look into weak references, which would allow eggs
to be deleted and their deletion later noticed via the weak references.
>
>>If you are in control over everything, there are going to be better ways, 
>>but it's
>>not clear whether you are trying to tweak some big stuff you don't want to 
>>bother fully
>>understanding, or whether you have creative control.
>
>The latter.  I'm an experienced developer working on code I have total 
>control over.
>
>Thanks in advance,
>
Always fun to explore ;-)

Regards,
Bengt Richter




More information about the Python-list mailing list