【问题标题】:How to structure classes such that I can easily inherit from a "Persistor" class which pickles the inherting object?如何构造类,以便我可以轻松地从腌制继承对象的“Persistor”类继承?
【发布时间】:2019-11-25 05:39:16
【问题描述】:

我正在编写一个新库,我想保留一些对象。我想使用 mixin 或某种适配器,所以我不必立即实现数据库。我现在正在使用 pickle 来存储对象。

假设我有一个 User 类。如果泡菜存在,我想从文件夹中加载用户。我写了一个 Persistor 类,它接受一个对象并将其写入指定的位置。我是否让 User 类继承自 Persistor 类?如果是这样,当用户类被实例化时,如果泡菜存在,我该如何用加载的对象替换该对象?还是我创建一个 UserPersistor 类?我只是想从 User 类中抽象出状态的加载和保存。

class User(Persistor???):
    """
    Central class to hold all user attributes and relationships.
    """

    def __init__(
        self,
        first_name: str,
        username: str,
        date_of_birth: datetime.date
    ):
        self.first_name = first_name
        self.username = username
        self.date_of_birth = date_of_birth
import pickle
import os


class Persistor:
    """
    Class whose job is to save and load state until we need a database.
    """

    def __init__(
        self,
        persistence_key
    ):
        self.persistence_key = persistence_key
        self.persistence_path = "data/" + persistence_key

    @property
    def save_exists(self):
        return os.path.exists(self.persistence_path)

    def save(self):
        outfile = open(self.persistence_path, 'wb')
        pickle.dump(self, outfile)
        outfile.close()

    def load(self):
        if self.save_exists:
            infile = open(self.persistence_path, 'rb')
            db = pickle.load(infile)
            infile.close()
            return db

    def delete(self):
        if self.save_exists:
            os.remove(self.persistence_path)

【问题讨论】:

    标签: python oop inheritance pickle mixins


    【解决方案1】:

    简单的答案(它不是特定于 Python 的 FWIW,这只是简单的 OO 设计):从语义上讲,继承表示“是”关系 - 所以如果 B 从 A 继承,那么 B 也是 A(参见 @ 987654321@ 也是)。所以真正的问题是:你会认为 UserPersistor 吗?

    另一个常见、明智且被广泛接受的设计原则是the single responsability principle,它规定每个组件都应该有一个单一的、明确定义的职责。所以问问自己,你是否真的希望持久性实现细节成为你 User 类职责的一部分......

    至于最后几个 OO 设计指南,它们在 GoF 的“设计模式”一书(实际上是 OO 设计 FWIW 的最佳文本之一)的(长)介绍中重复出现——我的意思是介绍,即使模式目录也很有趣):您应该更喜欢组合/委托而不是继承(从技术上讲,继承是组合/委托的受限形式),并且您应该始终对接口(Java术语中的“接口”-in大多数语言这意味着一个抽象基类),而不是一个实现(一个具体类)。通过将两者结合起来,您可以获得易于测试、维护和发展的良好解耦代码。例如,如果你让你的 User 类继承自 Persistor (这是一个具体的类),你不能轻易地切换到其他 Persistor 实现,而如果你使用组合/委托和一个抽象基类定义唯一的 Persistor API,您可以(理论上...)使用您的 User 类的任何 Persistor 实现。

    现在这是理论,现实是一些抽象往往有点泄漏 - 例如,要使用 pickle 协议进行持久化,您的 User 类必须是可腌制的,所以这不是 完全透明,所以对上述原则持保留态度 - 与其说是金科玉律,不如说是深思熟虑。就我而言,我可能会在这里使用组合/委托作为最安全的选择,并最终在实施过程中重新考虑整个设计,因为实施通常会突出设计缺陷(如果你至少可以发现它们的话)。

    编辑

    据我回忆,由于多重继承,您不需要在 Python 中使用接口。这是否会改变仅适用于 Python 的“是”规则?

    实际上,继承有两个目的:子类型化(如上所述),还有代码重用——这就是为什么 GoF 将继承描述为“一种受限制的组合/委托形式”(因为子类引用了它的父母并自动委托其部分操作)。

    子类型是 OO 的一个非常重要的部分,因为它构成了泛型代码和多态分派的基础 - 因此给定的代码片段可以与来自不同具体的对象集合一样工作 em> 类型(实现),只要它们实现相同的接口即可。

    语义,“接口”是一组已定义的(公共)功能(方法、属性、属性等)。现在,界面的技术如何定义取决于语言。

    静态类型语言需要一个类实现的接口的正式定义,以便它可以执行编译时检查并最终进行优化。请注意,我写了“interfaceS”——一个类可以实现多个接口,这实际上是一种相当常见的情况。

    对于支持多重继承的(静态)语言,显而易见的解决方案是使用抽象基类来定义接口。 Java 不支持多重具体继承(这是一种设计选择),因此它有一个名为“接口”的独特构造——但它实际上是纯抽象基类的另一个名称(“纯”:只有定义,根本没有实现)

    在动态语言中,实际上没有(技术上)需要正式的接口定义(因为没有正式的类型检查),所以“接口”是quite often informal - 您经常会在动态语言文档中找到“类似列表”之类的术语"、"file-like" 等(表示行为类似于列表或文件的对象)。

    虽然这在实践中效果很好(至少在大多数情况下),但对于正式的接口定义,仍然有一些好话要说,如果只是为了文档(以及简单的多态调度还不够的情况,以及你必须检查你的对象以找出你应该如何处理它们——考虑递归任意嵌套的字典和各种对象列表的结构)——所以 Python abstract base classes,它们是实际上推荐用于任何重要的项目。

    在所有情况下,“is a”关系在一定程度上仍然成立(即使根据 Liskov 的定义,子类不一定是正确的子类型),因为子类确实继承了它的父类隐含接口。

    注意:Python 也有“协议”,它们是一个类可以实现的更正式定义的特殊方法集,它们会给这个类的实例一些特殊行为(参见descriptorsiterators)。

    我可以让我的用户从 object 和 Persistor 继承吗?那是 python 的说法,它得到了那个功能?

    当然是的,你可以 - 对此没有技术限制,这只是一个设计问题:你的 User 类应该也是 Persistor,还是“更好”(取决于“更好”的定义)有两个明显不同的类 - 一个用于域模型(User),另一个用于持久性(例如 UserPersistor)。

    如果我将持久性逻辑委托给 Persistor 类,我仍然需要从我的 User 类代码中调用它

    不一定...如果您认为持久性不是用户的责任,那么您可以反过来看待事情:它是调用您的用户对象的持久性 - 在这种情况下,您实际上并没有甚至根本需要组合/授权。

    如果您自己获得了 GoF,我强烈建议您在继续您的设计决策之前花一些时间阅读它,并注意第一部分。只是一个警告:一旦你完成了,避免使用项目中的每一个模式,总是问自己这是否对手头的具体问题有意义,以及额外的复杂性(如果有的话)是否会永远对代码的这个特定部分有任何用处;-)

    【讨论】:

    • 非常感谢您的回复!刚买了四人帮的书。据我回忆,you don't need to use interfaces in Python 由于多重继承。这是否会改变仅适用于 Python 的“是”规则?我可以让我的用户从 object 和 Persistor 继承,这就是 python 的说法,它得到了这个功能?
    • 如果我将持久性逻辑委托给 Persistor 类,我仍然需要从我的 User 类代码中调用它。如果我从 Persistor 继承,我的用户不需要担心这一点。我还是有点卡住了。持久性逻辑应该放在哪里?我假设在 Persistor 类中。我唯一的问题是如何使用该类加载和保存我的对象。我需要做多重继承吗?或者我应该从我所有的班级委派给那个班级?
    • 哇!谢谢!让我消化一下,然后在这里回复你!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-11-11
    • 1970-01-01
    • 2011-04-04
    • 1970-01-01
    • 1970-01-01
    • 2011-04-21
    相关资源
    最近更新 更多