使用抽象类进行事件处理程序回调是惯用Python吗?

时间:2012-05-04 21:11:45

标签: python events callback

我正在构建一个事件调度程序框架,用于解码消息并回调用户代码。我的C ++背景表明我写道:

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()

然后框架用户会写一些类似的东西:

class MyHandler(Handler):
    def onFoo(self):
        do_something()

dispatcher = Dispatcher()
myHandler = MyHandler()
dispatcher.handler = myHandler
dispatcher.run()

但我也可以设想将onFoo()onBar()作为Dispatcher的方法,并让用户将其替换为其他方法。然后用户的代码如下:

def onFoo():
    do_something()

dispatcher = Dispatcher()
dispatcher.onFoo = onFoo

我还可以使Dispatcher.onFoo成为可调用的列表,因此用户可以像C#一样附加多个处理程序。

最恐怖的方式是什么?

1 个答案:

答案 0 :(得分:4)

我不会说第一种方式有什么特别错误的,特别是如果你想在运行时允许Dispatcher对象以有组织的方式进行自定义(即通过向其传递不同类型的Handler个对象,如果Handler需要与复杂状态进行交互并保持复杂状态。

但是你通过这种方式定义一个基类Handler类并没有真正获得任何好处;一个类仍然可以继承Handler而不覆盖任何方法,因为这不是一个抽象的基类 - 它只是一个常规的基类。因此,如果存在一些合理的默认行为,我建议将它们构建到Handler。否则,您的用户根本无法从Handler获得任何内容 - 他们也可以定义自己的Handler类。虽然如果你只是想提供一个无操作占位符,那么这个设置就可以了。

无论如何,我个人更喜欢你建议的第二种方法;我不确定这两者是否更“pythonic”,但第一个对我来说似乎更干净,因为它使HandlerDispatcher逻辑分开。 (它避免了你在threading.Thread中看到的情况,你只能安全地覆盖一些方法 - 我总是觉得有点刺耳。)

我觉得我应该指出,如果你真的想要一个真正的抽象基类,你应该写一个!从2.6开始,Python为灵活的abstract base classes提供了很多很好的功能。例如,您可以使用abstractmethod装饰器定义抽象方法,以确保子类必须重写;但您也可以定义不必重写的常规方法。如果你正在寻找一个C ++习语的Pythonic版本,这可能是最好的方式 - 当然,这不是一回事,但它比你现在的更接近。