Python中的委托
我写了一个简单的例子,想用来展示什么是委托模式。我的问题是,这样做是不是说明我理解了委托的概念呢?
class Handler:
def __init__(self, parent = None):
self.parent = parent
def Handle(self, event):
handler = 'Handle_' +event
if hasattr(self, handler):
func = getattr(self, handler)
func()
elif self.parent:
self.parent.Handle(event)
class Geo():
def __init__(self, h):
self.handler = h
def Handle(self, event):
func = getattr(self.handler, 'Handle')
func(event)
class Steve():
def __init__(self, h):
self.handler = h
def Handle(self, event):
func = getattr(self.handler, 'Handle')
func(event)
class Andy():
def Handle(self, event):
print 'Andy is handling %s' %(event)
if __name__ == '__main__':
a = Andy()
s = Steve(a)
g = Geo(s)
g.Handle('lab on fire')
3 个回答
我也在想这个问题,Python 似乎没有内置的委托功能。要是想用很多开源库来扩展这方面的功能,老实说,这样做有时候会有很大的质量风险,因为你在关键领域增加了对第三方库的依赖。当然,这完全取决于你使用的库是否在积极开发,但还是要小心。
很久以前,我通过 C# 学习了委托的概念,发现那里的文档解释得很好。
在评论中,我看到 Ashbay 建议你用 hasattr() 来确保你的类中存在某些函数。现在,既然你在多个类中使用委托,而不是简单的函数调用(参考 C#,那里有很好的解释),那么为什么不在你想提供委托的类中实现一个接口来增加“代码安全性”(这个建议真不错!)呢:
- 通过为你的真实类定义一个接口,你可以省去一次函数调用(节省一个处理过程)。
- 这个合同确保所有约定的内容在你的类层次结构中都得到了实现,从而满足约定的条件。
注意:接口通常是通过 Python 内置的 abc 包(抽象基类)来实现的——> 你可以查看为什么使用 abc 包(抽象类)来定义接口类有点笨拙:https://interface.readthedocs.io/en/latest/abc.html
不幸的是,这些功能还没有内置,但你可以参考这个链接:https://pypi.org/project/python-interface/#description
通过这个项目,你可以获得这种常见功能(为什么 Python 之前没有/现在仍然缺少这个功能,因为这是非常重要的面向对象编程基础!),因为外部库总是额外的依赖(可能会在不久的将来被弃用)。
你可以通过继承父类来使类更通用,通常通过接口可以将它们约定到父类,然后标记为抽象,并在需要实现时使用“pass”。只有在实现确实需要变化时,才应该这样做,否则就让它对所有类都通用(如果你继承很多类,这样可以节省大量编码时间和代码重复——> 因此,单独使用抽象基类和抽象方法是一种糟糕的模式)。
不确定 Python 是否提供像 C# 那样的虚拟重写功能,你可以“兼顾两者”,这样你只在真正需要时重写,然后默认使用父类的实现。大概率是可以做到的,但 Python 有点“叛逆”,很多东西做得稍微不同 :) 不过,以上链接中的接口 Python 包至少有一些支持。
另外,我还找到了一篇博客,讨论了这个话题并提供了多个选项:https://michaelcho.me/article/method-delegation-in-python
他提到的选项3中的委托者可能是指这个库:https://pypi.org/project/ObjectDelegator/
个人认为,如果你有时间更准确地设计你的类架构,通常情况下你根本不需要委托。委托应该更像是你在尝试让代码更通用和可用时的“最后手段”。不过,有时候(但很少)确实需要它们。
ps. 这是我第一次在 Stack Overflow 上评论,如果在某些外观和风格问题上没做到位,我很抱歉 :)
这里有一个Python的小技巧:你不需要这样写:
func = getattr(self.handler, 'Handle')
func(event)
只需要这样写:
self.handler.Handle(event)
我不太明白你在Handler类里做什么,因为在你的例子中并没有用到它。
另外,在Python中,方法名用大写字母的情况非常少见,通常是因为把一些已有的API移植过来,而那些API的名字就是这样。
没错,这就是基本的概念——把一些收到的请求转交给另一个对象去处理。