Event triggering and weak references

Ricardo Nogueira rnog2438 at mail.usyd.edu.au
Sun Apr 16 11:40:44 EDT 2000


Robert Getter <rgetter at usa.net> wrote in message
news:MPG.1363441c166d9ca98969e at news-server...
> In article <8dbif3$hm0$1 at metro.ucc.usyd.edu.au>,
> rnog2438 at mail.usyd.edu.au says...
> > First thanks for this beautiful language : )
> >
> > I am Smalltalk addicted : )  and this means that I can't do with any
other
> > language, ex. C, C++, Java,  : (
> > This was true until I found Python 2 weeks ago at Sydney Uni. I loved
its
> > modularity, pluggin behaviour and freedom.
> > But I am missing Smalltalk event handling... Smalltalk (like Python)
support
> > the call-Back mechanism, but there is a more powerful inter-object
interest
> > registering that I haven't found in Python (so far).
> >
> > This is what I would like to do:
> > Imagine I could have two or more Widgets open displaying a car speed
value,
> > say a numerical and a graphic display, if this car speed changes (ex.
some
> > one pushes the break), I would like to have any open widget on this
value
> > updated (on opening each window would register interest with this car
> > speed).
> >
> > Smalltalk implements this with:
> >
> > The widget would register interest in one aspect of the car  (on opening
> > over aCar):
> >   aCar when:#speedChanged send: #updateSpeedDisplay to: self
> >
> > And the car would just notify who is interested, if any (when its speed
> > changes):
> >   self trigger: #speedChanged
> >
> > But if any of this windows is not referenced anymore or crashes I would
like
> > the garbage collector clear it (the car would not be notified and his
> > pointer to the window shouldn't keep the garbage collector form freeing
the
> > window memory).
> >
> > Again, Smalltalk implements this with weak references, that doesn't
count
> > for the garbage collector.
> >
> > Is there support for this in Python?
> >
> > Any suggestions?
> >
> >
> >
>
> Here's some code which does some of what you want:
> =========
> # simple test of a scheme for registering interest in an event.
> # exception safety is left as an exercise for the reader
>
> class watcher:
> def __init__(self, watchee, voyeur=None):
> self.watchee = watchee
> if voyeur == None:
> self.list = []
> else:
> self.list = [ voyeur ]
>
> def __call__(self, *args, **kwargs):
> rc = apply(self.watchee, args, kwargs)
> for voyeur in self.list:
> apply(voyeur, args, kwargs)
> return rc
>
> def register(self, voyeur=None):
> if not voyeur: return
> self.list.append(voyeur)
>
> def unregister(self, voyeur=None):
> if voyeur: self.list.remove(voyeur)
> return len(self.list)
>
>
> def register(object, method, voyeur=None):
> # try to add this voyeur to the watcher object
> try:
> getattr(object, method).register(voyeur)
> # if this fails, there is no watcher object. Add one.
> except:
> try:
> setattr(object, method,
> watcher(getattr(object, method), voyeur))
> # if this fails, there is no method of that name. return
> None.
> except:
> return None
> # finally, return the newly installed watcher method
> return getattr(object, method)
>
> def unregister(object, method, voyeur=None):
> try:
> if not getattr(object, method).unregister(voyeur):
> # list is empty. remove watcher
> setattr(object, method,
> getattr(object, method).watchee)
> except: pass
>
> class test:
> def x(self): print "x()"
>
> def watch1():
> print "watch1()"
>
> def watch2():
> print "watch2()"
>
> def main():
> a = test()
> a.x()
> print
> register(a, "x", watch1)
> a.x()
> print
> register(a, "x", watch2)
> a.x()
> print
> unregister(a, "x", watch1)
> a.x()
> print
> unregister(a, "x", watch2)
> a.x()
>
> if __name__ == "__main__": main()
> ========
>
> What this code does is install a wrapper around the method you want to
> watch. This wrapper maintains a list of interested parties. There are a
> few problems with this method:
>
> 1. No weak references. I think there is a weak reference extension
> somewhere which could be added without too much trouble.
>
> 2. In Smalltalk, AFAIK, a method can only be referenced through its
> object. In Python, each method is a callable object which can be
> manipulated separately. In other words, object methods in Python are
> really instance variables which just happen to be callable. As a result,
> anywhere that objects pass bound methods around won't work right. If a
> bound method is passed before the wrapper is installed, calls to the
> method will not be sent to the watchers. This means that watchers can't
> be reliably watched, since they work by getting references to themselves
> put on a list in the watcher class.
>
> The only real solution I can think of for problem 2 is a generic
> mechanism for attaching watchers to any callable object. This is similar
> to what a debugger does. If you look into the debugging API, you may find
> something that is suitable or can be modified.
>
> I never much thought about it before, but this could be a really useful
> capability to have.
>
> Another way that this could be done which would also allow other nice
> things to happen would be the ability to globally change all references
> to one object into references to a different object.
> --
> Rob Getter

This is amazing! If I were this object "a" in your main method, I would feel
like a child that have his "coca-cola" swapped to a "cuba"! I do really have
lots to learn in this "do-what-ever-you-want-to-someones-object" filosofy. I
will keep this wrapper around the method in mind.
I implemented this observer pattern with a class that register interest from
any boundedMethod to be called when a named event occurs. Then the object
that inherits from this class has the option of triggering these events when
it wants. The advantage is more encapsulation, with colaboration between
objects. The disadvantage is that the trigger has to predict what will be
wanted from him in advance.
But both versions work with boundedMethods for the observer. If you change
your main() to register(a, "x" , objY.msg) it will keep objY from being
garbageCollected if we clear any other references to it. It is necessary to
always call unregister before the "last" pointer to objY is cleared. Not
very nice.

How can we read objY reference count?
If we come to know this number, compare to the number of boundedMessages we
have for it, we can conclude if it is garbage and clear its references
instead of calling its methods.

Still, weak references would do the job beautifully... : )

-----
RiNo





More information about the Python-list mailing list