17.8 总结
我们介绍了一些在设计模块和包时要考虑的点。在模块和单例类之间做了深入的对比。在设计一个模块时,数据结构和过程封装的一些基本问题与类设计时所考虑的是相关的。
当设计一个包时,尽量不使用过度嵌套的结构。当有多种实现时,我们就需要使用包;我们介绍了几种方式来应对实现的变化。有时需要定义一个包,将许多模块组合起来放入这个包中。我们介绍了如何使用 init .py来完成包内部的导入。
17.8.1 设计要素和折中方案
对于很深的包层次结构,可以将功能组织到定义的函数中。可以对所定义的函数和它们的相关数据进行组织,放入一个类中。我们可以将相关类组合为一个模块,并将相关模块组合为一个包。
当我们将软件理解为一种用于获取并表示信息的语言时,就需要思考类和模块所代表的是什么。模块为Python软件结构、分支、使用和重用的单元。为了减少异常,模块的设计必须考虑到可重用性。
对于大多数情况,我们会使用类,因为会需要创建类的多个实例。通常(但不是所有情况),类中会包含有状态的实例变量。
当考虑使用单例类时,如果真正需要类,那么使用单例的必要就不是很明显了。独立函数的存在可能和单例类的意义是相同的。在一些情况中,模块中独立的函数很适用,因为模块是可继承的单例。
在一般情况下,会使用有状态的模块——例如有状态的类。模块就是一个包含了可被修改的本地变量的命名空间。
当创建不可变类时(使用 slots,扩展tuple,或重写属性的setter方法),我们不能够轻易地创建一个不可变模块。几乎没有一个模块是不可变对象。
小型应用可以为单一模块。大型应用通常为一个包。由于模块化设计的需要,包通常被设计为可重用的。更大应用的包中应该包含一个 main模块。
17.8.2 展望
在下一章中,我们会综合应用一些面向对象的设计技巧。统观设计与实现的质量问题,其中一个考虑因素是要确保我们的软件是可信任的,而可信任的一个方面就是具备连贯的、易用的文档。
