简体   繁体   English

如何构造类以使我可以轻松地从使继承对象腌制的“ Persistor”类继承?

[英]How to structure classes such that I can easily inherit from a “Persistor” class which pickles the inherting object?

I'm writing a new library and I want to persist some objects. 我正在写一个新的库,我想保留一些对象。 I want to use a mixin or some sort of adapter so I don't have to implement a database right away. 我想使用mixin或某种适配器,所以我不必立即实现数据库。 I'm using pickle right now to store objects. 我现在正在使用泡菜来存储对象。

Let's say I have a User class. 假设我有一个User类。 I want to load a User from the folder if the pickle exists. 如果泡菜存在,我想从文件夹中加载用户。 I wrote a Persistor class which takes an object and writes it to a specified location. 我编写了一个Persistor类,该类接受一个对象并将其写入指定的位置。 Do I make the User class inherit from the Persistor class? 我可以使User类继承自Persistor类吗? If so, when the User class is instantiated, how then do I replace the object with the loaded object if a pickle eists? 如果是这样,当实例化User类时,如果出现泡菜,该如何用已加载的对象替换该对象? Or do I create a UserPersistor class? 还是创建UserPersistor类? I'm simply looking to abstract away the loading and saving of state from the User class. 我只是想从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)

Simple answer (and it's nothing Python-specific FWIW, this is just plain OO design): semantically, inheritance denotes a "is a" relationship - so if B inherits from A, then B is a A too (cf the Liskov substitution principle too). 简单的答案(这与Python特定的FWIW无关,这只是普通的OO设计):从语义上讲,继承表示“是”关系-因此,如果B继承自A,那么B也是A(参见Liskov替代原理) )。 So the real question is: would you consider that a User is a Persistor ? 因此,真正的问题是:您是否认为UserPersistor User

Another common, sensible and widely accepted design principle is the single responsability principle , which states that each component should have a single, well-defined responsability. 另一个常见,合理且被广泛接受的设计原则是单一责任原则 ,该原则指出每个组件都应具有一个明确定义的责任。 So ask yourself if you really want persistance implementation details to become part of your User class responsabilities... 因此,问问自己您是否真的希望持久性实现详细信息成为您的User类职责的一部分...

As for a last couple OO design guidelines, which are repeated all over the (long) introduction of the GoF's "Design patterns" book (actually one of the best texts on OO design FWIW - the introduction I mean, even if the patterns catalog is of interest too): you should favor composition/delegation over inheritance (inheritance being, technically speakin, a restricted form of composition/delegation), and you should always program to an interface ("interface" in the Java term - in most languages this means an abstract base class), never to an implementation (a concrete class). 至于最后一对面向对象设计指南,在GoF的“设计模式”一书的(长期)介绍中反复进行(实际上是关于面向对象设计FWIW的最佳文字之一-即使模式目录是同样也是您感兴趣的):您应该更喜欢使用组合/委托而不是继承(从技术上讲,继承是一种受限的组合/委托形式),并且应该始终编程为接口(Java术语中的“接口”-在大多数语言中,表示抽象基类),而不是实现(具体类)。 By combining both, you get well decoupled code that's easy to test, maintain and evolve. 通过将两者结合在一起,您将获得易于测试,维护和发展的良好分离的代码。 For example, if you make your User class inherit from Persistor (which is a concrete class), you cannot easily switch to other Persistor implementation, while if you use composition/delegation and an abstract base class defining the only the Persistor API, you can (theoretically...) use any Persistor implementation your User class. 例如,如果使User类继承自Persistor (这是一个具体类),则无法轻松切换到其他Persistor实现,而如果使用组合/委托和仅定义Persistor API的抽象基类,则可以(理论上...)使用您的User类的任何Persistor实现。

Now that's for the theory, and reality is that some abstractions tend to be kind of leaky - for example, to be persisted using the pickle protocol, your User class has to be pickleable, so this is not totally transparent, so take the above principles with a grain of salt - more as food for thought than golden rules, that is. 现在是理论上的问题,现实是某些抽象趋向于泄漏-例如,要使用pickle协议进行持久化,您的User类必须是可腌制的,因此这不是完全透明的,因此请遵循上述原理带有一粒盐-比起黄金法则,它更是值得思考的食物 As far as I'm concerned I would probably use composition/delegation as the safest choice here, and eventually reconsider the whole design during implementation progress, as implementation often hilights design flaws (if you can spot them at least). 就我而言,我可能会在这里使用组合/委托作为最安全的选择,并最终在实施过程中重新考虑整个设计,因为实施通常会掩盖设计缺陷(如果至少可以发现它们)。

EDIT 编辑

From what I recall, you don't need to use interfaces in Python due to multiple inheritance. 我记得,由于多重继承,您不需要在Python中使用接口。 Does that change the "is a" rule just for Python? 这会改变仅适用于Python的“是”规则吗?

Actually, inheritance serves two purposes: subtyping (as explained above), but also code reuse - which is why the GoF describes inheritance as "a restricted form of composition/delegation" (in that the child class has a reference to it's parent(s) and automagically delegates parts of it's operations). 实际上,继承有两个目的:子类型化(如上所述),还有代码重用-这就是GoF将继承描述为“组合/委托的受限形式”的原因(子类对父类的引用) )并自动委派其部分操作)。

Subtyping is a very important part of OO, since it forms the basis for generic code and polymorphic dispatch - so a given piece of code can work just the same with a collection of objects that are from different concrete type (implementation), as long as they implement the same interface. 子类型化是OO中非常重要的一部分,因为它构成了通用代码和多态调度的基础-因此给定的代码段可以与来自不同具体类型(实现)的对象集合相同地工作,只要它们实现相同的接口。

Semantically , an "interface" is a defined set of (public) features (methods, attributes, properties etc). 语义上 ,“接口”是一组定义的(公共)功能(方法,属性,属性等)。 Now how an interface is technically defined depends on the language. 现在,如何在技术上定义接口取决于语言。

Statically typed languages need a formal definition of the interfaces a class implements so it can perform compile-time checks and eventually optimizations. 静态类型语言需要类实现的接口的正式定义,以便它可以执行编译时检查并最终进行优化。 Note that I wrote "interfaceS" - a class can implement more than one single interface, and that's actually a rather common case. 请注意,我写了“ interfaceS”-一个类可以实现多个接口,这实际上是一种很常见的情况。

For (static) languages that support multiple inheritance, the obvious solution is to use abstract base classes for interface definitions. 对于支持多重继承的(静态)语言,显而易见的解决方案是将抽象基类用于接口定义。 Java doesn't support multiple concrete inheritance (that's a design choice) so it has a distinct construct named "interfaces" - but it's really another name for pure abstract base classes ("pure": only definitions, no implementation at all) Java不支持多重具体继承(这是一种设计选择),因此它有一个独特的名为“ interfaces”的构造-但这实际上是纯抽象基类的另一个名称(“ pure”:仅定义,完全没有实现)

In dynamic languages, there's actually no (technical) need for formal interface definitions (since there's no formal typechecking), so "interfaces" are quite often informal - you'll often find in dynamic languages docs terms like "list-like", "file-like" etc (to denote objects that behaves like lists or files). 在动态语言中,实际上并不需要(技术上的)正式接口定义(因为没有正式的类型检查),因此“接口” 通常是非正式的 -在动态语言中,您经常会发现诸如“类似列表”,“文件状”等(表示行为类似于列表或文件的对象)。

Now while this works well in practice (most of the times at least), there are still good things to be said about formal interface definitions, if only for documentation (and also for the cases where simple polymorphic dispatch is not enough and you have to inspect your objects to find out what you should do with them - think recursing over a structure of arbitrarly nested dicts and lists of various objects) - so Python also have abstract base classes , and they are actually recommended for any non-trivial project. 现在,尽管这在实践中行之有效(至少在大多数情况下是这样),但是对于形式化接口定义而言,仍然有很多要说的东西,即使仅用于文档(以及仅用于简单多态分派还不够,并且您必须检查您的对象以了解您应该如何处理它们-考虑对任意嵌套的字典和各种对象列表的结构进行递归操作)-因此Python 具有抽象基类 ,实际上,建议将它们用于任何不重要的项目。

In all cases, the "is a" relationship still holds to a certain degree (even if the child class is not necessarily a proper subtype according to Liskov's definition) since a subclass does inherit it's parent(s) class(es) implied interface(s). 在所有情况下,“子关系”仍然保持一定程度(即使子类不一定按照Liskov的定义是正确的子类型),因为子类确实继承了其父类的隐式接口( S)。

NB : Python also have "protocols", which are more formally defined sets of special methods a class can implement and that will give instances of this class some special behaviours (cf descriptors or iterators ). 注意:Python也有“协议”,它们是类可以实现的特殊方法的正式定义集,它们将为该类的实例提供一些特殊行为(参见描述符迭代器 )。

Can I have my User inherit from object and Persistor and that would be python's way of saying that it gets that functionality? 我可以让我的User从object和Persistor继承,这将是python表示获得该功能的方式吗?

Well of course yes you can - there's no technical restriction on this, it's just a design question: should your User class also BE a Persistor, or would it be "better" (depending on the definition of "better") to have two clearly distinct classes - one for the domain model (User), and a technical one for persistance (UserPersistor for example). 当然,是的,您可以-没有技术限制,这只是一个设计问题:您的User类也应该是Persistor,还是“更好”(取决于“更好”的定义),使两个明确不同的类-一种用于域模型(用户),另一种用于持久性(例如UserPersistor)。

If I delegate the persistence logic to the Persistor class, I still need to call that from my User class code 如果我将持久性逻辑委托给Persistor类,则仍需要从User类代码中调用它

Not necessarily... If you decide that persistance is not the User's responsability, then you can just see things the other way round: it's the Persistor that calls on your User object -and in this case, you actually don't even need composition/delegation at all. 不一定...如果您确定持久性不是用户的责任,那么您可以反过来查看:持久性调用您的User对象-在这种情况下,您实际上甚至不需要组合/授权。

If you got yourself the GoF, I strongly suggest you take some time reading it before you go on with your design decisions, paying attention to the first part. 如果您自己拥有GoF,我强烈建议您在继续进行设计决策之前花一些时间阅读它,并注意第一部分。 Just a warning though : once you're done, avoid the tentation to use each and all of the patterns in your project, always ask yourself if it makes sense for the concrete problem at hand and whether the extra complication (if any) will ever be of any use for this particular part of your code ;-) 不过只是警告:完成后,避免使用项目中所有和所有模式的意图,请始终问问自己对于当前的具体问题是否有意义以及是否会产生额外的复杂性(如果有)对您代码的这一特定部分没有任何用处;-)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 包含其他类的超级结构/类,可以轻松传递 - super structure/class that contains other classes, that can be easily passed around 在Django中,如何轻松地继承字段并使用此字段的预配置参数创建新类? - In Django, how can I easily inherit a field and create a new class with this field preconfigured parameters? 如何在继承了python pytest中的UnitTest类的类中使用fixtures - How to use fixtures in a Class which is inherting UnitTest class in python pytest 如何腌制从TLorentzVector继承的类? - How to pickle classes which inherit from TLorentzVector? 如何继承继承 db.Model 的 class - How can I inherit a class which inherits db.Model 如何在pyqt中的类之间继承? - How can I inherit between classes in pyqt? 如何构造从一个 BaseClass 继承的类的方法? - How to structure methods of classes that inherit from one BaseClass? 我如何在 C# 中反序列化 python 泡菜? - How I can deserialize python pickles in C#? 如何从另一个类继承实例参数/方法? - How can i inherit instance parameters /methods from another class? Django Paginator-我在继承自Paginator的类中创建了一个函数,以及为什么不能在前端使用此函数 - Django Paginator - I create a function in a class inherting from Paginator and why I can't use this function in front-end
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM