返回介绍

钻石继承变动

发布于 2024-01-29 22:24:15 字数 3157 浏览 0 评论 0 收藏 0

也许新式类中最显著的变化就是,对于所谓的多重继承树的钻石模式(diamond pattern)的继承(也就是有一个以上的超类会通往同一更高的超类)处理方式有点不同。钻石模式是高级设计概念,在Python编程中很少用到,并且在本书中目前为止还没有讨论过,因此我们没有必要深入讨论。

简而言之,对经典类而言,继承搜索程序是绝对深度优先,然后才是由左至右。Python一路往上搜索,深入树的左侧,返回后,才开始找右侧。在新式类中,在这类情况下,搜索相对来说是宽度优先的。Python先寻找第一个搜索的右侧的所有超类,然后才一路往上搜索至顶端共同的超类。换句话说,搜索过程先水平进行,然后向上移动。搜索算法也比这里介绍的更复杂一些,但是,大多数程序员了解这些就够了。

因为有这样的变动,较低超类可以重载较高超类的属性,无论它们混入的是哪种多重继承树。此外,当从多个子类访问超类的时候,新式搜索规则避免重复访问同一超类。

钻石继承例子

为了说明起见,举一个经典类构成的简单钻石继承模式的例子。这里,D是B和C的超类,B和C都导向相同的祖先A:

此处是在超类A中内找到属性的。因为对经典类来说,继承搜索是先往上搜索到最高,然后返回再往右搜索:Python会先搜索D、B、A,然后才是C(但是,当attr在A找到时,B之上的就会停止)。

这里,对于派生自object这样的内置类的新式类,以及Python 3.0中的所有类,搜索顺序是不同的:Python会先搜索C(B的右侧),然后才是A(B之上):也就是先搜索D、B、C,然后才是A(在这个例子中,则会停在C处)。

这种继承搜索流程的变化是基于这样的假设:如果在树中较低处混入C,和A相比,可能会比较想获取C的属性。此外,这也是假设C总是要覆盖A的属性:当C独立使用时,可能是真的,但是当C混入经典类钻石模式时,可能就不是这样了。当编写C时,可能根本不知道C会以这样的方式混入。

在这个例子中,很可能程序员认为C应该覆盖A,尽管如此,新式类先访问C。否则,C将会在钻石环境中基本无意义:它不会定制A,并且只对同名的C使用。

明确解决冲突

当然,假设的问题就是这是假设的。如果难以记住这种搜索顺序的偏好,或者如果你想对搜索流程有更多的控制,都可以在树中任何地方强迫属性的选择:通过赋值或者在类混合处指出你想要的变量名。

在这里,经典类树模拟了新式类的搜索顺序:在D中为属性赋值,使其挑选C中的版本,因而改变了正常的继承搜索路径(D.attr位于树中最低的位置)。新式类也能选择类混合处以上的属性来模拟经典类。

如果愿意以这样的方式解决这种冲突,大致上就能忽略搜索顺序的差异,而不依赖假设来决定所编写的类的意义。

自然,以这种方式挑选的属性也可以是方法函数(方法是正常可赋值的对象)。

在这里,我们明确在树中较低处赋值变量名以选取方法。我们也可以明确调用所需要的类。在实际应用中,这种模式可能更为常用,尤其是构造函数。

这类在混合点进行赋值运算或调用而做的选择,可以有效地把代码从类的差异性中隔离出。通过这种方式明确地解决冲突,可以确保你的代码不会因以后更新的Python版本而有所变化(除了在Python 2.6中,新式类需要从object或内置类型派生类以使用新式工具之外)。

注意:即使没有经典/新式类的差异,这种技术在一般多重继承场合中也很方便。如果你想要左侧超类的一部分以及右侧超类的一部分,可能就需要在子类中明确使用赋值语句,告诉Python要选择哪个同名属性。我们会在本章结尾的陷阱中再介绍这个概念。

此外,钻石继承模式在有些情况下的问题,比此处所提到的还要多(例如,如果B和C都有所需的构造函数会调用A中的构造器,那该怎么办呢?),由于这样的语境在Python中很罕见,已不是本书范围之内(请参见super定制函数以获得提示——除了提供对单继承树中的超类的通用性访问,super还支持一种协作模式,以解决多继承树中的一些冲突)。

搜索顺序变化的范围

总而言之,默认情况下,钻石模式对于经典类和新式类进行不同的搜索,并且这是一个非向后兼容的变化。此外要记住,这种变化主要影响到多继承的钻石模式情况。新式类继承对于大多数其他的继承树结构都是不变的工作。此外,整个问题不可能在理论上比实践中更重要,因为,新式类搜索直到Python 2.2才足够显著地解决,并且在Python 3.0中才成为标准,它不可能影响到太多的Python代码。

正如已经提到的,我还应该注意到,即便你没有在自己编写的类中用到钻石模式,由于隐式的object超类在Python 3.0中的每个类之上,所以如今多继承的每个例子都展示了钻石模式。也就是说,在新式类中,object自动扮演了我们前面所讨论的实例中类A的角色。因此,新的类搜索规则不仅修改了逻辑语义,而且通过避免多次访问相同的类而优化了性能。

同样重要的是,新模式中的隐式object超类为各种内置操作提供了默认方法,包括__str__和__repr__显示格式化方法。运行一个dir(object)来看看提供了哪些方法。没有一个新式的搜索顺序,在多继承情况中,object中的默认方法将总是覆盖用户编写的类中的重新定义,除非这些重定义总是放在最左边的超类之中。换句话会说,新类模式自身使得使用新搜索顺序更关键!

要了解Python 3.0中隐式object超类的一个更清楚示例,以及该对象所创建的钻石模式的其他示例,参见上一章lister.py示例中的ListTree类的输出,以及第28章中的classtree.py示例。

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据
    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文