471,073 Members | 1,391 Online
Bytes | Software Development & Data Engineering Community
Post +

Home Posts Topics Members FAQ

Join Bytes to post your question to a community of 471,073 software developers and data experts.

Events

Anyone want to comment on what they think of this following bit of a
simple event framework?
Are there any really obvious things I should be doing with this code
that stop me from getting bitten on the arse in the future?
class Talker:
def __init__( self ):
self.listeners= []

def AddListener( self, event, listener ):
self.listeners.append( [ event, listener ] )

def RemoveListener( self, event, listener ):
for i, ( e,l ) in enumerate( self.listeners ):
if (e == event ) and ( l==listener ):
self.listeners.pop( i )

def Talk( self, event, *args ):
for i in self.listeners:
evt, listener= i
if evt == event:
listener( *args )
class Listener:
def HeardSomething( self, message ):
print 'Listener.HeardSomething'
print message

class FoobarTalker( Talker ):
importantEvent= 42

def SomethingHappened( self, something ):
self.Talk( FoobarTalker.importantEvent, something )

l= Listener()
t= FoobarTalker()

t.AddListener( FoobarTalker.importantEvent, l.HeardSomething )
t.SomethingHappened( 'something' )
t.RemoveListener( FoobarTalker.importantEvent, l.HeardSomething )
-Factory
Jul 18 '05 #1
5 1386
factory wrote:
Anyone want to comment on what they think of this following bit of a
simple event framework?
Are there any really obvious things I should be doing with this code
that stop me from getting bitten on the arse in the future? .... class FoobarTalker( Talker ):
importantEvent= 42

def SomethingHappened( self, something ):
self.Talk( FoobarTalker.importantEvent, something ) ....

Rather than making events be numbers (simple discrete things),
I suspect that you'd be happier with a hierarchy. This way
You can listen to "all kinds of events."

Python gives you a nice way to define hierarchies in classes,
so you could use classes directly as your events, or make
your events instances (and thus allow data to be attached to
the event). Certainly if you are attaching data, and possibly
even if you you are not, you may want to provide a means of
retrieving "the current event." You could even look at the
history of exceptions in python for a model of what you might want.

So, for events:
class Event(object): pass # The mother event in case you need to share
class ImportantEvent(Event): pass
class IgnorableEvent(Event): pass
class UsefulEvent(Event): pass

you could change the guts of your Talk method from:
.... evt, listener = i
if evt == event:
listener(*args)

To:
....
criterion, listener = i
if issubclass(event, criterion):
listener(*args)

Or (for the data-attached form):
criterion, listener = i
if isinstance(event, criterion):
listener(*args)
....
Jul 18 '05 #2
> Are there any really obvious things I should be doing with this code

You might want to think about defining an event class. There's a
cookbook recipe that addresses this kind of thing. It is at
http://aspn.activestate.com/ASPN/Coo.../Recipe/286136

I've been playing with a little program that I'm going to use in a
paper on event-driven programming. It uses an event-class. Also uses
a set rather than a list to hold the list of
observers/subscribers/listeners:

************************************************** *************

import sets

class Dispatcher:
def __init__( self ):
# initialize a set of observers
# each observer includes the event-type that
# it wants to observer
self.myEventHandlers = sets.Set()

def registerHandler( self, argEventHandler, argEventType ):
self.myEventHandlers.add( (argEventHandler, argEventType) )

def eventArrival(self, argEvent):
# dispatch the event to the registered handlers
self.notifyEventHandlers(argEvent)

def notifyEventHandlers( self, argEvent):
for anEventHandler, anEventType in self.myEventHandlers:
if anEventType == argEvent.myEventType:
# if the observer is registered for this type of event...
anEventHandler.handle(argEvent) # ...send the event to it

class Event:
def __init__(self, argEventType, argEventSource):
self.myEventType = argEventType
self.myEventSource = argEventSource

class EventHandler:
def __init__(self, argHandlerName):
self.name = argHandlerName

def handle(self, argEvent ):
# for this simple example, all we do to handle
# the event is print information about it
print self.name , "is handling a", argEvent.myEventType, \
"event from" , argEvent.myEventSource
# create a dispatcher
demoDispatcher = Dispatcher()

# create some event handlers
demoHandler1 = EventHandler("Larry")
demoHandler2 = EventHandler("Moe ")
demoHandler3 = EventHandler("Curly")

# define an event type
demoEventType = "LeftMouseDoubleClick"

# register the event handlers for that type of event
demoDispatcher.registerHandler( demoHandler1, demoEventType )
demoDispatcher.registerHandler( demoHandler2, demoEventType )
demoDispatcher.registerHandler( demoHandler3, demoEventType )

# generate an actual event
demoEvent = Event(demoEventType, "mouse") # "mouse" is the event
source

# send the event to the dispatcher
demoDispatcher.eventArrival(demoEvent)

************************************************** **************
Jul 18 '05 #3
factory <t@t.com> writes:
Anyone want to comment on what they think of this following bit of a
simple event framework?


You should definitely look into the dispatcher module:

http://aspn.activestate.com/ASPN/Coo...n/Recipe/87056

The comments indicate that an enhanced version is maintained on SF.

Thomas
Jul 18 '05 #4

"factory" <t@t.com> wrote in message
news:MPG.1c08708d5ec6746c989688@news-server...
Anyone want to comment on what they think of this following bit of a
simple event framework?


Since there can be multiple listeners for a particular type of event, I
would use an event-listener dict with event as key and list of listeners as
the value. This simplifies the code. Plus, linearly scanning a list of
event listener pairs to respond to an event (or deregister) will bite when
the list gets long enough. So, in __init__,
self.listeners = {}

For AddListener, the handy idiom for appending a new listener to a possibly
non-existent list of current listeners is
self.listeners.get(event, []).append(listener)
(If you cannot other wise guarantee that listeners only try to register for
a particular event once, you might want to add a check here.)

The body of Talk is then the much simpler
for listener in self.listeners[event]:
listener( *args )

If you can guarantee that listeners are only registered once for an event,
then deletion is also simple: self.listeners[event].remove(listener)

Terry J. Reedy


Jul 18 '05 #5
Terry Reedy wrote:
For AddListener, the handy idiom for appending a new listener to a possibly
non-existent list of current listeners is
self.listeners.get(event, []).append(listener)


This bit doesn't actually work.

When event isn't found in self.listeners, you create a new, empty
list.... but that list is never bound as a value in self.listeners. And
even if you add a 'self.listeners = ...' to that line, you'll still have
the problem that list.append() returns None -- you'll overwrite
self.listeners with that None.

If you use listeners.setdefault() instead, though, it works just as you
have it --
listeners = {}
listeners.setdefault('OnClick', []).append('foo')
listeners {'OnClick': ['foo']}


The difference between get() and setdefault() seems to be that get()
simply returns a sentinel value if the requested key is missing, but
setdefault() will create a key and assign that value to it, and then
return the (default) value of the (new) key.

Jeff Shannon
Technician/Programmer
Credit International
Jul 18 '05 #6

This discussion thread is closed

Replies have been disabled for this discussion.

Similar topics

6 posts views Thread by Saso Zagoranski | last post: by
14 posts views Thread by JPRoot | last post: by
2 posts views Thread by Bob Rundle | last post: by
30 posts views Thread by Burkhard | last post: by
5 posts views Thread by Richard Maher | last post: by
1 post views Thread by swethak | last post: by

By using Bytes.com and it's services, you agree to our Privacy Policy and Terms of Use.

To disable or enable advertisements and analytics tracking please visit the manage ads & tracking page.