5

我正在构建一个事件调度程序框架,用于解码消息并回调到用户代码中。我的 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()andonBar()作为方法Dispatcher并让用户用其他方法替换它们。然后用户的代码将如下所示:

def onFoo():
    do_something()

dispatcher = Dispatcher()
dispatcher.onFoo = onFoo

我还可以Dispatcher.onFoo列出可调用对象,这样用户就可以像在 C# 中那样附加多个处理程序。

最Pythonic的方法是什么?

4

1 回答 1

4

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

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

无论如何,我个人更喜欢第一种方法而不是您建议的第二种方法;我不确定哪一个更“pythonic”,但第一个对我来说似乎是一种更干净的方法,因为它保持HandlerDispatcher逻辑分开。(它避免了像你看到的那种threading.Thread你只能安全地覆盖某些方法的情况——我总是觉得这有点不和谐。)

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

于 2012-05-04T21:35:42.557 回答