Event triggering and weak references
Robert Getter
rgetter at usa.net
Sun Apr 16 04:31:01 EDT 2000
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
More information about the Python-list
mailing list