Events

F

factory

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
 
S

Scott David Daniels

factory said:
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)
....
 
S

Stephen Ferg

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/Cookbook/Python/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)

****************************************************************
 
T

Terry Reedy

factory said:
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
 
J

Jeff Shannon

Terry said:
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
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
474,211
Messages
2,571,092
Members
47,694
Latest member
digiadvancemarketing

Latest Threads

Top