Python:我应该在这里使用委托还是继承?

4

我在思考是否应该使用继承还是委托来实现一种包装类。我的问题是这样的:假设我有一个名为Python的类。

class Python:

    def __init__(self):
        ...

    def snake(self):
        """ Make python snake through the forest"""
        ...

    def sleep(self):
        """ Let python sleep """
        ...

...以及更多的行为。现在我有一些现有的代码,它期望一个Anaconda,这几乎就像一个Python,但是有些成员有稍微不同的名称和参数,其他成员添加了新的功能。我真的想在Python中重用这些代码。因此,我可以通过继承来实现:

class Anaconda(Python):

    def __init__(self):
        Python.__init__(self)

    def wriggle(self):
        """Different name, same thing"""
        Python.snake(self)

    def devourCrocodile(self, croc):
        """ Python can't do this"""
        ...

当然,我也可以调用Anaconda().sleep()。但问题在于:我需要使用一个名为PythonFactory的东西。
class PythonFactory:

    def makeSpecialPython(self):
        """ Do a lot of complicated work to produce a special python"""return python

我希望用Python创建一个工具,并且能够将其转换成Anaconda

myAnaconda = Anaconda(PythonFactory().makeSpecialPython())

在这种情况下,委托是最好的选择。(我不知道是否可以使用继承来实现):
class Anaconda:

    def __init__(self, python):
        self.python = python

    def wriggle(self):
        self.python.wriggle()


    def devourCrocodile(self, croc):
        ...

但是使用委托,我无法调用Anaconda().sleep()

因此,如果您还在跟我一起,我的问题是:

A)在类似这种情况下,当我需要

  • 添加一些功能
  • 重命名一些功能
  • 否则使用“基类”功能
  • 将“基类”对象转换为“子类”对象

我应该使用继承还是委托?(或其他什么?)

B)一种优雅的解决方案是使用委托加上一些特殊方法,将 Anaconda 无法响应的所有属性和方法访问转发到其 Python 实例。


2
我不明白为什么你没有一个共同的基类。 - Marcin
我没有自己的代码。我正在使用“Anaconda”、“Python”和现有的“PythonFactory”代码,无法修改。我需要在两组现有代码之间进行“适应”,使它们能够兼容。 - clstaudt
谁将使用您的蛇对象?他期望从对象中获得哪个接口或接口?这些接口之间如何相关? - Kos
"Anaconda" 应该提供的接口是由我正在重新实现的现有代码库决定的(尽量保持代码相似)。 - clstaudt
Anaconda 是否应该同时执行 snake() 或仅执行 wriggle() 并拒绝执行 snake(),就好像它没有被实现一样? - Tobias Kienzler
1个回答

7
B) 一种优雅的解决方案是使用委托加上一些特殊的方法,将Anaconda没有响应的所有属性和方法访问转发给Python实例。在Python中很简单,只需定义__getattr__
class Anaconda:

    def __init__(self, python):
        self.python = python

    def wriggle(self):
        self.python.snake()


    def devourCrocodile(self, croc):
        ...

    def __getattr__(self, name):
        return getattr(self.python, name)

请参考Python文档中 __getattr__ 章节。

2
附注:适配器设计模式似乎是问题的理想解决方案。它与委托相关,但更具体。mensi的答案是一个适配器的Python实现...尽管在那种形式中wriggle()的显式声明是不必要的(除非它的主体应该调用self.python.snake())。+1 :) - Walter
@Walter,这实际上是问题来源所做的事情,因此我假设存在一个小错误并进行了更正。现在问题是,是否允许Anaconda直接执行snake(),否则可能需要使用def snake(self): raise NotImplementedError()(或者return NotImplemented?虽然这可能会导致Python.snake最终被评估...) - Tobias Kienzler

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接