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

jap*_*iss 5 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()
Run Code Online (Sandbox Code Playgroud)

然后框架用户会写如下:

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

dispatcher = Dispatcher()
myHandler = MyHandler()
dispatcher.handler = myHandler
dispatcher.run()
Run Code Online (Sandbox Code Playgroud)

但我也可以设想把onFoo()onBar()作为方法,Dispatcher并让用户用其他方法替换它们.然后用户的代码看起来像:

def onFoo():
    do_something()

dispatcher = Dispatcher()
dispatcher.onFoo = onFoo
Run Code Online (Sandbox Code Playgroud)

我还可以制作Dispatcher.onFoo一个callables列表,这样用户就可以在C#中附加多个处理程序.

什么是最恐怖的方式呢?

sen*_*rle 4

我不会说第一种方法有什么特别错误的Dispatcher地方,特别是如果您希望允许在运行时以有组织的方式自定义对象(即通过Handler向它们传递不同类型的对象),如果您Handler需要与复杂状态交互并维持复杂状态。

但是通过这种方式定义基类并没有真正获得任何好处Handler;一个类仍然可以Handler在不重写任何方法的情况下进行子类化,因为这不是一个抽象基类——它只是一个常规基类。因此,如果有一些合理的默认行为,我建议将它们构建到Handler. 否则,您的用户根本不会从中获得任何东西Handler——他们还不如定义自己的Handler类。不过,如果您只想提供一个无操作占位符,那么这种设置就可以了。

无论如何,我个人更喜欢您建议的第一种方法而不是第二种方法;我不确定这两者是否更“Pythonic”,但第一个对我来说似乎是一种更干净的方法,因为它保持Handler逻辑Dispatcher分离。(它避免了像您所看到的threading.Thread只能安全地覆盖某些方法的情况——我总是发现这有点刺耳。)

不过,我觉得我应该指出,如果您真的想要一个真正的抽象基类,您应该编写一个!从 2.6 开始,Python 提供了对灵活抽象基类的支持,并具有许多不错的功能。例如,可以用abstractmethod装饰器定义一个抽象方法,这保证了它必须被子类重写;但您也可以定义不必重写的常规方法。如果您正在寻找 C++ 习惯用法的 Pythonic 版本,这可能是最好的方法——当然,它不是同一件事,但它比您现在拥有的更接近。