I am building an event dispatcher framework that decodes messages and calls back into user code. My C++ background suggests that I write:
class Handler:
def onFoo(self):
pass
def onBar(self):
pass
class Dispatcher:
def __init__(self):
self.handler = Handler()
def run():
while True:
msg = decode_message() # magic
if msg == 'foo':
handler.onFoo()
if msg == 'bar':
handler.onBar()
Then the framework user would write something like:
class MyHandler(Handler):
def onFoo(self):
do_something()
dispatcher = Dispatcher()
myHandler = MyHandler()
dispatcher.handler = myHandler
dispatcher.run()
But I can also imagine putting onFoo() and onBar() as methods of Dispatcher and letting the user replace them with other methods. Then the user’s code would look like:
def onFoo():
do_something()
dispatcher = Dispatcher()
dispatcher.onFoo = onFoo
I could also make Dispatcher.onFoo a list of callables, so the user could attach more than one handler like in C#.
What is the most Pythonic way to do it?
I wouldn’t say that there’s anything particularly wrong about doing it the first way, especially if you want to allow
Dispatcherobjects to be customized in an organized way at runtime (i.e. by passing different kinds ofHandlerobjects to them), if yourHandlers need to interact with and maintain complex state.But you don’t really gain anything by defining a base
Handlerclass this way; a class could still subclassHandlerwithout overriding any methods, because this isn’t an abstract base class — it’s just a regular base class. So if there are some sensible default behaviors, I would suggest building them in toHandler. Otherwise, your users don’t gain anything fromHandlerat all — they might as well just define their ownHandlerclass. Though if you just want to provide a no-op placeholder, then this set-up is fine.In any case, I would personally prefer the first approach to the second that you suggest; I’m not certain that either is more “pythonic,” but the first seems like a cleaner approach to me, since it keeps
HandlerandDispatcherlogic separate. (It avoids situations like the one you see inthreading.Threadwhere you can only safely override some methods — I’ve always found that a bit jarring.)I feel I should point out, though, that if you really want a bonafide abstract base class, you should write one! As of 2.6, Python provides support for flexible abstract base classes with lots of nice functionality. For example, you can define an abstract method with the
abstractmethoddecorator, which ensures that it must be overridden by the subclass; but you can also define regular methods that don’t have to be overridden. If you’re seeking a Pythonic version of the c++ idiom, this is probably the best way to go — it’s not the same thing, of course, but it comes closer than what you have now.