Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is it idiomatic Python to use an abstract class for event handler callbacks?

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?

like image 271
japreiss Avatar asked Oct 07 '22 22:10

japreiss


1 Answers

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 Handlers 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.

like image 109
senderle Avatar answered Oct 12 '22 12:10

senderle