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