对事件处理程序回调使用抽象类是否是Python的惯用方法?
我正在构建一个事件调度器框架,该框架将消息解码并调用回用户代码。我的C++背景表明我写的是:对事件处理程序回调使用抽象类是否是Python的惯用方法?,python,events,callback,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:
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#一样附加多个处理程序
什么是最具python风格的方法?我不会说第一种方法有什么特别错误,特别是如果您希望在运行时以有组织的方式自定义
调度程序对象(即通过向它们传递不同类型的处理程序对象),如果您的处理程序
需要与复杂状态交互并维护复杂状态
但是,通过这样定义一个基本处理程序类,您并没有真正获得任何好处;类仍然可以在不重写任何方法的情况下子类化Handler
,因为这不是一个抽象基类——它只是一个常规基类。因此,如果存在一些合理的默认行为,我建议将它们构建到处理程序中。否则,您的用户根本不会从Handler
中获得任何收益——他们也可以定义自己的Handler
类。不过,如果您只想提供一个无操作占位符,那么这个设置就可以了
无论如何,我个人更喜欢第一种方法,而不是你建议的第二种方法;我不确定这两种方法是否更“pythonic”,但第一种方法对我来说似乎更干净,因为它将Handler
和Dispatcher
逻辑分开。(它避免了像您在threading.Thread
中看到的那种只能安全地重写某些方法的情况——我总是发现这有点不协调。)
我觉得我应该指出,如果你真的想要一个真正的抽象基类,你应该写一个!从2.6版开始,Python提供了对具有许多优秀功能的flexible的支持。例如,您可以使用abstractmethod
decorator定义一个抽象方法,这确保它必须被子类重写;但您也可以定义不必重写的常规方法。如果你正在寻找一个Python版本的C++习惯用法,这可能是最好的方法,当然,这不是一回事,但它比你现在的更接近。我不太在乎抽象基类。我想提供一个默认的无操作行为,这样用户就不必处理每一个不想处理的事件。@japreiss,啊,我明白了——这很有意义。我不是那样想的。我不确定它是否具有通灵性;就其价值而言,我更喜欢第一个版本,但我不认为其中一个版本比另一个版本更像蟒蛇。有更好的方法吗?Twisted似乎对事件处理程序使用类继承。我没有用Python做太多事件驱动的东西,所以我不确定这是否是“正常”的方式。@japreis,我认为这真的取决于。我以前与PyGTK合作过,并且使用过这两种方法。如果处理程序需要维护自己的状态(我经常这样做),那么类就很有意义,我倾向于使用这种方法。但是有时候你只想定义一个简单的函数——甚至是一个lambda
——在这种情况下,基于类的方法可能会有些过分。是的,这也是我的想法。一个6个,另一个半打。有相当多的状态需要管理,因此使用类让我感觉更好。这是Python的缺点之一:它支持太多的编码风格,有时很难选择?已经有很多了。我并没有建立任何通用的东西——我可能让这个项目听起来比实际情况更大。它只是一个消息传递层,用于解码协议缓冲区消息并调用适当的函数。为了进一步扩展:我希望避免过度依赖第三方库,因为我已经在将它们集成到公司庞大的专有代码库中时遇到了一些困难。我想保留删除库并用“发明代码”替换它们的可能性。