Python的多继承和和Scala的trait

在业务设计过程中,除了继承这种增量进化,有些时候我们只需要给类添加功能而不是想变成某种类型,那么我们可以选择组合。在这篇文章会先介绍Python的多继承和Scala的trait对组合的实现,最后再来讨论两者的优劣和如何更好的使用它们。

python

那么从一段Python代码开始,看看Python如何处理组合的问题,以及我们要如何避免多继承的问题。

这里的C同时继承了A,B的方法a,b(为了页面展示省去了内置方法),bases方法给出了C继承的父类A和B。

对于多继承,还需要解决子类继承的多个父类的实现了同一个方法的问题,这个问题就是“菱形问题”,对上面的代码再做一定的修改:

NewB和B都继承了A,那么继承了B和NewB的C调用的是哪个方法?Python对于多继承同名方法的调用是基于C3算法实现的,这篇文章不对C3算法做深入的了解,简单来说就是:

深度优先,从左到右,移除继承列表中重复类型,保留最后一个。

在实际过程中,可以使用__mro__方法查看这个类的方法解析顺序。下面的代码也证明了C中的b方法来源于NewB这个类。

当然我们也可以跳过方法继承顺序直接使用我们想要的类,唯一要做的就是显性的传入self这个参数。例如:

不过最好的办法是使用super(),遵循方法继承顺序,不容易引起混乱。

注意的是,使用Python的多继承的过程中,极力避免子类继承多个不同类型的类,如果某个类是一个混入类,在其后面加上Mixin。

Scala

了解完Python的多继承,再来讨论Scala的trait的使用。

一个简单的特质A实现了,我们可以使用extends或者是with加入到类B当中:

用extends关键字实际上是表示B隐性的继承了特质A

特质也可以作为一个类型来使用,例如:

在某个类已经继承了某个父类的时候,就需要使用with关键字来混入trait了。(可以连续使用with来混入多个特质)

Scala的特质可以像类一样的定义,但是不能传入任何构造参数,例如:

同样的trait也面临着继承顺序的问题,于Python不同的是,Scala的继承顺序关键在于super方法的调用,而不是同名方法的调用顺序的确定,因为添加进子类的两个trait的同名方法在编译期便会报错。

我们设计这么一个类来展示Scala如何确定super的调用方法:

从代码中可以看出来首先起作用的是特质C,其次是B,最后A,再将BCA放入buf列表当中,Scala将这种调用顺序称为线性化,它将所有类和它继承的类以及特质按照一定顺序排列起来,从右至左开始执行。

如何评价:

继承可以让新手顺利的使用专家设计出来的框架,但是其本身基于依赖的实现方式会导致耦合的问题。所以很重要的一点就是,在需求的实现过程中,应该区分我们要做的是功能扩展还是使用某些功能,如果仅仅只是使用某些功能(组合),所以Scala和Python给出了两种不同的实现方式,Scala选择了trait(特质),Python因为历史原因,保持了多继承的方式。它们都提供了一种混入(mix-in)的机制,让某一种类型的扩展出一种其它类型的功能。多继承的问题在于,它会导致写出来的类产生混乱,无法判断你继承的类到底属于哪一种类型。Python的多继承在一定程度上并没有Scala的灵活,它的多继承在处理同名方法时采用的是覆盖的方式,而组合的核心在于“能做什么”,而不是“是什么”,功能的混入不应该像类的继承,而是相对独立,正因为如此,trait逐渐成为了现在语言的发展趋势。

  • 发表于:
  • 原文链接http://kuaibao.qq.com/s/20180401G17X1C00?refer=cp_1026
  • 腾讯「腾讯云开发者社区」是腾讯内容开放平台帐号(企鹅号)传播渠道之一,根据《腾讯内容开放平台服务协议》转载发布内容。
  • 如有侵权,请联系 cloudcommunity@tencent.com 删除。

扫码关注腾讯云开发者

领取腾讯云代金券