4.8 总结、设计要素和折中方案

    在本章中,我们介绍了抽象基类中最重要的部分。对于每种抽象基类,我们都介绍了它们的一些特性。

    我们也学习到一个好的类设计中应该尽可能地使用继承。我们使用了两大不同的模式,也看了这条原则的一些特殊情况。

    一些程序中的类需求的行为无法重用Python内置的特性。在我们21点的例子中,一张牌不是一个数值类型、一个容器、一个迭代器或者一个上下文,它只是一张牌。在这个例子中,通常我们会自定义一个新类,因为没有任何内置的特性可以继承。

    但是,当我们看Hand类时,我们会发现它很明显是一个容器。正如我们在第1章“init()方法”和第2章“与Python无缝集成——基本特殊方法”中所叙述的,下面是3个基本的设计原则。

    • 封装一个现有的容器。
    • 扩展一个现有的容器。
    • 创建一个全新的容器。

    大多数情况下,我们会选择封装或者扩展一个现有的容器,这和我们尽可能使用继承的原则一致。

    当我们扩展一个现有的类时,我们的自定义类能够很好地融入这个现有类的层次结构中。例如,一个扩展了内置的list类就已经是collections.abc.MutableSequence的一个实例了。

    但是,当我们封装一个现有的类时,我们必须仔细地考虑原始接口中有哪些部分我们想要继续保留,有哪些部分我们不想保留。在之前章节的例子中,对于封装的list对象,我们只提到了pop()方法。

    由于封装好的新类不是一个完整的不可变序列的实现,所以有很多特性它无法支持。另一方面,一个扩展类在很多情况下往往更有用。例如,一个扩展了listHand类天生就是可迭代的。

    如果我们发现扩展一个类不足以满足我们的需求,我们可以考虑创建一个全新的集合类型。至于新创建的一个集合类型必须提供哪些方法才能与现有的Python特性无缝集成,抽象基类的定义中提供了许多建议。在第6章“创建容器与集合”中,我们会给出一个创建集合类型的详细例子。

    展望

    在后面的章节中,我们会大量地使用本章中讨论的这些抽象基类。在第5章“可调用对象和上下文的使用”中,我们会介绍可回调对象和上下文中一些相对简单的特性。在第6章“创建容器和集合”中,我们会介绍Python中内置的容器和集合,同时也会介绍如何创建一种独特的新容器。最后,在第7章“创建数值类型”中,我们会介绍不同的数值类型,并且会介绍如何自定义数值类型。