这段时间都在忙着考试,很久没有更新了,感谢各位蛇友的不离不弃。今天我们来学习python面向对象编程中的继承与多态。
继承
语法
继承,顾名思意,就是子类继承父类的方法和属性等,先来看看继承的语法结构。
继承机制
属性的继承下面我们来定义一个父类——Person,再定义一个子类——Male(继承自Person类),看看Male是否能从Person中继承属性(废话,不能继承你还能写啥?)
可以看到,Male类中除了pass占位语句之外没有定义任何东西,但是却拥有属性name和age,也就是说Male类确实继承了Person类中的属性。方法继承我们来做几个实验,看看子类对父类三种方法(即实例化方法,类方法,静态方法)的继承结果。主要代码结构
然后我们分别通过实例化对象male对三种方法进行调用以便验证是否继承。实例化方法验证
类方法验证
静态方法验证
可以看出,子类实例化对象可以调用三种方法,即子类把所有方法都继承下来了。上面是针对父类的属性和方法都是公有的情况进行讨论,对于属性和方法是半私有和私有的还没有进行讨论,但其实这个问题在上一期对属性和方法的访问中就已经进行过深入的讨论了,此处再来简单简述一下结果: 子类可以继承父类半私有的属性和方法,但是无法继承父类的私有属性和方法。我们可以通过dir()方法来看看子类继承自父类的属性和方法公有
半私有
私有
很明显,只有当子类和父类中的属性和方法名称相同时,才说明子类继承了父类的属性和方法,从三种结果可以看出,父类的私有属性和方法确实不能被子类所继承。多继承上面的例子其实基本上是对上期内容的回顾,只是开胃小菜,下面来看今天的重头戏。我们可以看出,上面的例子都是单继承,而python面向对象编程还支持多重继承,下面我们来了解一下语法主要有三种情况:情况一
情况二
情况三
情况一主要是想和各位读者说明一下存在这种情况而已,其用法可以根据前面的单继承举一反三即可,下面我们着重来看看第二和第三种情况,因为这存在顺序的先后问题。为了演示简单,下面的例子的类名采用大写字母A、B、C、D......来表示。首先来看一个简单的例子,C类继承A类和B类,而A、B、C类拥有相同名字的函数,当C的实例化对象调用该函数时,到底是调用ABC哪个类的函数呢?
揭晓答案:
答案显而易见,调用的是自己本身的函数。那么当C类中没有该同名函数时会发生什么呢?
我们来揭晓答案
可以看到,它调用的是A类的方法,也就是就近原则。我们再进一步把A中的方法名改变一下,其它地方不动,再来看看输出结果是什么。
可以看到,现在调用的是B中的方法,因为当A中没有该方法时,子类就会去B类中搜索方法,如果B中有该方法则调用,如果没有则报错。简单对第二种情况做个总结,当子类继承多个父类时,在子类和父类有相同名字的函数时,如果子类调用该方法,则调用的是子类本身的方法,如果子类没有该函数,则根据就近原则(写在前面的父类)依次检索并调用父类中的该函数,如果所有父类中都没有该方法,则报错。我喜欢把第二种情况称为双重继承,下面我们来看看第三种情况,而这种情况,我喜欢称之为多重继承,这就比较复杂了。同样,按上面第二种情况的思路,我们来看看下面这个简单的例子。
这个很简单,打印的就是class C,如果C中没有该方法就去B中检索,如果B中存在该方法则调用,如果没有则去A中检索,如果有则调用,没有则报错。一句话就是:层层往上检索,有则调用,无则报错。当然,这是比较简单的一种情况,下面我们来看一个比较复杂的。
在这个例子中,D类本身以及最近的父类都没有my_print()方法,而其第二个父类(C类)以及第一个父类的父类(A类)拥有该方法,那么D类调用的是A类还是C类的方法呢?这个要看情况,如果是python3,则打印的是class C,如果是python2,则打印的是class A。python3版本
现在我把版本切换至2.7
可以看到,同一段程序,在python2和python3中的结果是不一样的。为什么会这样呢?这就涉及到深度优先算法和C3算法的问题了。python2遵循的是深度算法,python3遵循的是C3算法,两种算法可以用下面两张图来表示
在python2中,所有类默认为经典类,不过可以显式继承object变成新式类,而在python3中,所有类默认继承object,即默认为新式类。如果我们在python2的环境下,让A类显式继承object这个最上层根类,那么其结果和python3中的结果一样。
当然,上面的例子还是相对比较简单,所以C3算法看起来和广度优先算法差不多,下面我们来看看下面这个更加复杂的例子,你就可以更加透彻地掌握C3算法了。
这个例子的继承关系比较复杂,我给大家画个图来捋一捋。
深度优先算法我们就不讨论了,打印的肯定是class B,我们主要来讨论一下新式类,根据上图两种情况,可以知道,如果打印结果是class E,则表示按照第一种检索顺序,而如果打印的是class B,则表示是第二种检索顺序,那么python3中到底遵循的是哪一种检索顺序呢?多说无益,我们来看一下打印结果
可以看到,结果是class B,也就是说python3遵循的是第二种检索顺序,其实第一种是广度优先算法,而第二种才是C3算法,这里主要是想给大家介绍一下这两种算法的差异。其实C3算法还是比较复杂的,如果我们想要快速知道它的检索顺序,可以通过mro()方法查看,比如:
从打印结果可以看出,其遵循的顺序是F>D>B>E>C>A,正好符合我们的第二种检索顺序。
多态多态的含义用平白的话说,多态就是子类覆写父类的方法,这样的好处是相同名称的方法在不同的子类中有不同的行为。这就好比你和你的兄弟姐妹继承了你父亲的吃的这个行为,但是你们的口味是不同的,你父亲喜欢吃辣的,你喜欢吃甜的,而你兄弟姐妹喜欢吃咸的。代码实例
可以看到,相同的方法,不同的行为,这就是多态。当然,你还可以传参。
鸭子类型
可能很多人都听说过鸭子类型,但是一直不熟悉,今天我们就来简单讲讲。只有像python这种变量类型没有提前声明的动态语言才有鸭子类型这一说法,像java和c这种静态语言就不存在鸭子类型。虽然我们对鸭子类型不熟悉,但其实我们很早就使用鸭子类型了,比如python的内置函数len(),就属于鸭子类型的应用,我们知道,len()的参数可以是列表,也可以是字符串等等,也就是说我们不关心类型,只关心行为结果,只要参数可迭代即可。这就像鸭子类型的来源一样:如果一只鸟跑起来像鸭子,游泳起来像鸭子,叫起来也像鸭子,那么这只鸟就可以被称为鸭子。super()一提到super(),可能大家就会想到这是调用父类的方法,但其实它是一个类,而且也不是用来调用父类,而是用来调用ORM的下一个节点。值得注意的是super()函数只能在新式类中使用,下面代码的运行环境除了特殊说明外均为python3。代码实例
可能有人会问了,这又什么,不就是调用父类吗?那和下面这段代码有什么区别呢?
没错,在比较简单的继承关系中,这是几乎没有什么区别的,但是如果是在多继承中,那么两者的差别就很大了,看下面例子。类名调用
super()调用
可能有些读者无法理解super()调用的第三步到第四步,我这里简单说明一下,前面我们已经说过了,super()并不是调用父类,而是调用ORM(也就是继承链)的下一个节点,而B的下一个节点正是C。
本期内容有点多,祝大家学习愉快!!!
领取专属 10元无门槛券
私享最新 技术干货