Python中的委托

22 投票
3 回答
37313 浏览
提问于 2025-04-16 03:43

我写了一个简单的例子,想用来展示什么是委托模式。我的问题是,这样做是不是说明我理解了委托的概念呢?

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 个回答

1

我也在想这个问题,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 上评论,如果在某些外观和风格问题上没做到位,我很抱歉 :)

17

这里有一个Python的小技巧:你不需要这样写:

func = getattr(self.handler, 'Handle')
func(event)

只需要这样写:

self.handler.Handle(event)

我不太明白你在Handler类里做什么,因为在你的例子中并没有用到它。

另外,在Python中,方法名用大写字母的情况非常少见,通常是因为把一些已有的API移植过来,而那些API的名字就是这样。

10

没错,这就是基本的概念——把一些收到的请求转交给另一个对象去处理。

撰写回答