This simple class implements the observer design pattern. Acting as a registration hub, it fires simple Events when requested.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 | from weakref import ref
class Event(object):
def __init__(self, name, stuff=None):
self.name = name
self.stuff = stuff
def __str__(self):
return '%s(%s)'%(self.name,self.stuff)
class Observer(object):
listeners={}
weakListeners={}
def register(self, eventName, callback, isWeak=False):
if not isWeak: listenDict = Observer.listeners
else: listenDict = Observer.weakListeners
callbacks = listenDict.get(eventName)
if not callbacks:
callbacks = []
listenDict[eventName] = callbacks
if not isWeak and callback not in callbacks:
callbacks.append(callback)
elif isWeak:
for cb in callbacks:
refobj = cb()
if refobj and refobj==callback:
return
callbacks.append(ref(callback))
def unregister(self, eventName, callback, isWeak=False):
if not isWeak: listenDict = Observer.listeners
else: listenDict = Observer.weakListeners
callbacks = listenDict.get(eventName)
if callbacks:
if not isWeak:
try: callbacks.remove(callback)
except: pass
else:
cblist = []; cblist.extend(callbacks)
for cb in cblist:
refobj = cb()
if refobj and refobj==callback:
callbacks.remove(cb)
elif not refobj:
callbacks.remove(cb)
def fireEvent(self, event):
if not event: return
callbacks = []
callbacks.extend(Observer.listeners.get(event.name,[]))
for cb in Observer.weakListeners.get(event.name,[]): callbacks.append(cb())
for cb in callbacks:
if cb: cb(event)
def fireEventName(self, name, stuff=None):
self.fireEvent(Event(name,stuff))
observer = Observer()
|
I use this to send application event notifications between system components (not distributed and synchronized processing. Its works fine for that purpose. It doesn't account for threads, so you has to watch inter-thread communication carefully (as usual :-)
UPDATE: I added support for weak references.
Check out related recipes. Broker and broadcaster for loose coupling:
http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/81983
C#-style events:
http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/410686
Multicasting:
http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/52289
A by-the-book observer pattern implementation:
http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/131499
Why callbacks list in fireEvent function. I am not able to understand why 'callbacks' list is needed in fireEvent function. Instead you can just have it like this:
cbs = Observer.listeners.get(event.name)
in register you are guaranteeing a callback list for the event, so you
either get None or a list on the above statement.
if cbs:
--Hemanth
Misnomer. I'd rather call this class an "event dispatcher", because it is independent of the objects that actually fire the events (probably because they changed their state). Objects subscribed to the dispatcher would then have to know for themselves, what the semantics of the event they received are, i.e what/who has actually changed.
The term observer/observable or publisher/subscriber is usually used for a pair of classes that define a similar but somewhat different interface, as the observable (publisher) class is usually used as a base class for objects, wanting to notify others about changes in their _own_ state. These objects would normally correspond to the "model" part of an MVC pattern.
The observer (subscriber) only needs to provide a method (usually called "update") that is called whenever the publisher fires an event. It then either has to ask the publisher about it's new state or the new state is already provided when update() is called ("pull" resp. "push").
Recipe 131499 mentioned by the first comment implements the technique I described.
Need the callbacks list due to possible looping. If the callback invoked changes the list of registered objects, you will need a copy of the list. Otherwise some callbacks may not be invoked. This is the case when you register for an event, then unregister when the event occurs.
Gotcha. Point taken. For my purposes, this was an simple implementation for a specific design need. The semantics and subtlies wheren't given much thought.
Watch out, the weak references support is treacherous. Bound methods won't work with weak refs, see http://code.activestate.com/recipes/81253/