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 Dispatcher
objects to be customized in an organized way at runtime (i.e. by passing different kinds of Handler
objects to them), if your Handler
s need to interact with and maintain complex state.
But you don't really gain anything by defining a base Handler
class this way; a class could still subclass Handler
without 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 to Handler
. Otherwise, your users don't gain anything from Handler
at all -- they might as well just define their own Handler
class. 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 Handler
and Dispatcher
logic separate. (It avoids situations like the one you see in threading.Thread
where 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 abstractmethod
decorator, 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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With