2009-01-25

Function call multiplexing in python

I ran into a bit of a snag today when learning PyQt - I had n listeners registered with my object, and when an event occurred, I had to run (notify) all of them. Now, I'm not a big fan of for-loops and it got me thinking about how I could run them all and have neat syntax at the same time.

What I came up with was this:

class Multiplexer(object):
def __init__(self, objects, function):
self.objects = objects
self.function = function

def __call__(self, *args, **kwargs):
return [getattr(object, self.function)(*args, **kwargs) for object in self.objects]

Naturally, some people asked "Why __call__? Why not dispatch() or something similar?"

The answer is pretty straight-forward, really - I wanted my code to look like this:
# listeners is a collection of listener objects
listeners.gotGasEvent(newValue1, newValue2)

This would, ideally, call *each* of the listener's gotGasEvent() functions and thereby notify their respective notifyees of the event. What this line does looks obvious, with no clutter and to the point. Still, I had some more things to sort out before such juggling would be possible. This was solved by the following ListenerCollection class:

class ListenerCollection(object):
def __init__(self):
self.listeners = {}
def addListener(self, name, listener):
self.listeners[name] = listener

def removeListener(self, name):
del self.listeners[name]

def __getattribute__(self, attribute):
try:
return object.__getattribute__(self, attribute)
except AttributeError:
return Multiplexer( self.listeners.itervalues(), attribute )
The code is pretty straightforward - there's a dictionary of listeners, and the only thing it forbids you is that you can't name an event "removeListener" or "addListener". The neat thing is that we can now do what we wanted to do above without any problems:

class IAmEventful(object):
def __init__(self):
self.listeners = ListenerCollection()

def feedMe(self, food):
# Phone people gave food!
self.listeners.receivedFood(food)
There we go, a thousand listeners served. (with food)

Regards,

Gašper

No comments:

Post a Comment