【问题标题】:Can a class inherit methods (or subclass) from a different file? Is this "Pythonic"?一个类可以从不同的文件继承方法(或子类)吗?这是“Pythonic”吗?
【发布时间】:2017-01-09 11:22:33
【问题描述】:

在尝试构建一个科学的 Python 包时,我的目标是将关键功能分离到不同的 .py 文件中以清楚起见。具体来说,将模型的复杂数值计算拆分为一个 python 文件(例如,“processing.py”)并将其可视化的例程绘制到另一个 python 文件(“plotting.py”)中,这对我来说似乎是合乎逻辑的。

为了方便和节省内存使用,模型类应该能够继承所有绘图方法,使用户可以轻松访问它们,同时通过将科学数字代码与可视化代码分开来保持代码易于维护和阅读。

我在下面概述了实现这一目标的愿景,但我正在努力解决如何在 Python 中实现这一目标/如果有更好的 OOP 样式可用。

例如在plotting.py中:

class ItemPlotter(object):

    def visualisation_routine1(self):
        plt.plot(self.values)  # where values are to be acquired from the class in processing.py somehow

在 processing.py 中:

class Item(object):

    def __init__(self, flag):
        if flag is True:
            self.values = (1, 2, 3)
        else:
            self.values = (10, 5, 0)

        from plotting.py import ItemPlotter
        self.plots = ItemPlotter()

导致以下命令行用法:

 from processing.py import Item
 my_item = Item(flag=True)
 # Plot results
 my_item.plots.visualisation_routine1()

我的实际代码会比这更复杂,并且可能 Item 将具有大型数据集的属性,因此我希望我需要避免复制这些以提高内存效率。

请问我的设想是否可行,或者甚至是 Pythonic 方法? 任何 cmets 重新。将不胜感激 OOP 或实现此目标的有效方法。

PS,我的目标是兼容 Py2.7 和 Py3。

【问题讨论】:

  • 这既不是继承也不是子类化,这是组合。是的,使用其他地方的类的实例作为属性很好,但您仍然应该将导入放在文件的顶部。
  • 您能提供一个工作示例吗?当我尝试这个时,我得到 AttributeError: 'ItemPlotter' 对象没有属性 'vales'。感谢您的帮助。
  • 嗯...是的,没错,它没有。因为,再次,你没有子类化。您有一个ItemPlotter 实例您的Item 实例中,但它们不是同一个对象(它们不共享self)。也许Item应该继承自ItemPlotter,反之亦然。
  • 或者,您可以在实例化 ItemPlotter 或调用 visualisation_routine1 时传递 Item 的实例(即 self)。
  • 还要注意创建例如self.plotter = ItemPlotter(self.values) 实际上并没有复制 值,它只是意味着两个实例共享对同一个对象的引用。但是,如果您希望 ItemPlotter 成为“混合类”,则需要 Item 来继承它。

标签: python class oop inheritance


【解决方案1】:

为了方便和节省内存使用,模型类应该能够继承所有绘图方法,使用户可以轻松访问它们,同时通过将科学数字代码与可视化代码分开来保持代码易于维护和阅读。

正如 Jon 指出的那样,您实际上是在描述组合而不是继承(无论如何,这是 generally preferred 的做法)。

实现您想要的一种方法是创建一个抽象类,该类定义“项目绘图仪”和inject it as a dependencyItem 实例的接口。您可以允许Item 的客户端通过将委托 绘图的方法暴露给注入的ItemPlotter 来轻松绘制Item 实例封装的数据。

这种方法允许每个类尊重Single Responsibility Principle,并使代码更易于理解、维护和测试。如果您愿意,可以在单独的 Python 模块中定义 ItemItemPlotter

from abc import abstractmethod, ABC


class AbstractItemPlotter(ABC):
    @abstractmethod
    def plot(self, values):
        return


class ItemPlotter(AbstractItemPlotter):
    def plot(self, values):
        # plt.plot(values)
        print('Plotting {}'.format(values))
        pass


class Item(object):
    def __init__(self, flag: bool, plotter: AbstractItemPlotter):
        self._plotter = plotter
        if flag:
            self.values = (1, 2, 3)
        else:
            self.values = (10, 5, 0)

    def visualisation_routine1(self):
        self._plotter.plot(self.values)

if __name__ == '__main__':
    item = Item(flag=True, plotter=ItemPlotter())
    item.visualisation_routine1()

输出

Plotting (1, 2, 3)

编辑

此代码使用 Python 3.5.2 进行了测试。我不能确定它是否可以在 Python 2.7 中按原样工作。

【讨论】:

  • 感谢这个以及指向良好 OOP 样式的链接。这看起来很棒。我想知道为什么我们需要创建 AbstractItemPlotter 类 - 我是否需要将 AbstractItemPlotter 中的 ItemPlotter 的每个方法复制为“抽象方法”才能工作?此外,我想知道您认为这与@Daniel Roseman 在上一篇评论中的想法相比如何。简单地使用 self.plotter = ItemPlotter(self) 初始化绘图仪类,并在 ItemPlotter init 中保存对 self 的引用?谢谢
  • @IanRoberts “我是否需要复制 AbstractItemPlotter 中 ItemPlotter 的每个方法作为“抽象方法”才能正常工作?”只有在实现之间可能会有所不同的方法。通用方法可以在基类中实现。另见this post。至于第二个问题,Daniel 的方法似乎是另一种不错的做法,尽管我不知道他是否真的打算将ItemPlotter 存储在Item 中。这使课程更加紧密,这通常很糟糕。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2017-03-15
  • 2019-06-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-06-03
  • 1970-01-01
相关资源
最近更新 更多