类的继承
基本概念
- 面向对象三要素之一,继承Inheritance
- 举例:
- 人类和猫类都继承自动物类
- 个体继承自父母,继承了父母的一部分特征,但也可以有自己的个性
- 在面向对象的世界中,以父类继承,就可以直接拥有父类的属性和方法,这样可以减少代码、多复用。子类可以定义自己的属性和方法
- 上面的类虽然有关系,但是定义时并没有建立这种关系,而是各自完成了定义,动物类和猫类都有吃,但是他们的吃有区别,所以分别定义
- 上例可以看出,通过继承,猫类、狗类不用写代码,直接继承了父类的属性和方法
- class Cat(Animal)这种形式就是从父类继承,括号中写上继承的类的列表
- 继承可以让子类从父类获取特征(属性和方法)
父类 - Animal就是Cat的父类,也称为基类、超类
子类 - Cat就是Animal的子类,也称派生类
定义
- class 子类名(基类1[,基类2,……])
- 语句块
- 如果类定义时,没有基类列表,等同于继承自object。在Python3中,object类是所有对象的根基
- Python支持多继承,继承也可以多级
- 查看继承的特殊属性和方法
特殊属性和方法
|
含义
|
示例
|
__base__
|
类的基类
|
|
__bases__
|
类的基类元祖
|
|
__mro__
|
显示方法查找顺序,基类的元祖
|
|
mro()方法
|
同上
|
ini.mro()
|
__subclasses__
|
类的子类列表
|
int.__subclasses__()
|
继承的访问控制
- 父类继承,自己没有的,就可以到父类中找
- 私有的都是不可以访问的,但是本质上依然是改了名称放到这个属性所在类的__dict__中。知道这个新名称就可以直接找到这个隐藏变量,慎用
总结:继承时,公有的,子类和实例都可以随意访问;私有成员被隐藏,子类和实例不可以直接访问。只有私有变量所在的类内的方法可以访问这个私有变量Python通过自己一套实现,实现和其他语言一样的面向对象的继承机制属性的查找顺序:实例的__dict__ —>类__dict__ —>父类__dict__如果搜索这些地方后没有找到就会抛异常
方法的重写、覆盖override
super()可以访问到父类的属性,原理未知
- 方法覆盖,原理一样,属性字典的搜索顺序
继承中的初始化
- 上列代码可知:
- 如果类B定义时声明继承自类A,则在类B中的__bases__中是可以看到类A
- 但是这和是否调用类A的构造方法是两回事
- 如果B中调用了A的构造方法,就可以拥有父类的属性了
- 作为好习惯,如果父类定义了init,就应该在子类的init中调用它
- 如果子类中没有定义init,则自动调用父类init,如下:
- 如果子类中有init方法则不会自动调用父类的初始化init,需要手动调用
- 下例中打印10,原因看__dict__就知道了,因为父类Animal中的show方法中__age会被解释成_Animal__age,因此显示的是10,而不是11
多继承
- ocp原则:多继承,少修改
- 继承的用途:增强基类、实现多态
- 在面向对象中,父类、子类通过继承联系在一起,如果可以通过一套方法,就可以实现不同表现,就是多态
- 一个类继承自多个类就是多继承,它将具有多个类的特性
- 弊端:
- 多继承很好的模拟了世界,因为事物很少是单一继承,但是舍弃简单,必然引入复杂性,带来了多冲突
- 如同一个孩子继承了来自父母双方的特征,那么到底眼睛像爸还是像妈?应该像谁多一些
- 多继承的实现会导致编译器设计的复杂度增加,所以很多语言舍弃了多继承
- C++支持多继承,Java舍弃了多继承
- Java中,一个类可以实现多个接口,一个接口也可以继承多个接口。java的接口很纯粹,只是方法的声明,继承者必须实现这些方法,就具有了这些能力,就能干什么。
- 多继承可能会带来二义性,例如,猫和狗都继承自动物类,现在如果一个类多继承了猫和狗类,猫和狗都有了shout方法,子类究竟继承了谁的shout?
- 解决方案
- 实现多继承的语言,要解决二义性,深度优先或者广度优先
Python多继承实现
- class Classname(基类列表):
- 类体
- 多继承带来路径选择问题,究竟继承哪个父亲的特性呢。
- Python使用MRO(method resolution order)解决基类搜索顺序问题
- MRO三个搜索算法:
- 经典算法,按照定义从左到右,深度优先 2.2之前
- 上左图的MRO:Myclass->D->B->A->C->A
- 新式类算法,经典算法的升级,重复的只保留最后一个。2.2
- 上左图MRO:Myclass->D->B->C->A,object
- C3算法,在类被创建出来的时候,就计算出一个MRO有序列表,2.3之后,Python3唯一支持的算法
- 上左图MRO:Myclass->D->B->C->A,object
- C3算法解决了多继承的二义性
多继承的缺点
- 经典算法,按照定义从左到右,深度优先 2.2之前
- 当类很多,继承复杂的情况下,继承路径太多,很难说清什么样的继承路径
- Python语法是允许多继承,但是Python代码是解释执行,只有执行到的时候才发现错误
- 团队协作开发,如果引入多继承,代码将不可控
- 不管编程语言是否支持多继承,都应当避免多继承
- Python的面向对象,我们看到的太灵活,太开放,所以需要团队守规矩
- MRO三个搜索算法:
Mixin
- 分析:
- 看似不错,如果需要还要提供其他能力,如何继承?
- 应用于网路哦,文档应该具备序列化的能力,泪上就应该实现序列化
- 可序列化还可能分为使用pickle,json,messagepack等
- 这时候发现,类可能太多,继承的方式不是很好了
- 功能太多,A类需要某几样功能,B类需要另几样功能,很繁琐
- 装饰器
- 用装饰器增强一个类,把功能给类附加上去,哪个类需要,就装饰它
- 使用装饰器装饰类的好处是:简单方便,在需要的地方动态增加
- Mixin就是其他类混合进来,同时带来了类的属性和方法
- 这里看来Mixin类和装饰器的效果一样,但是Mixin可以继承
- Mixin本质上就是多继承
- Mixin体现的是一种组合模式
- 在面向对象的设计中,一个复杂的类,往往需要很多功能,而这些功能有来自不同的类提供这就需要很多的类组合在一起
- 从设计模式的角度来说,多组合少继承
- Mixin使用原则
- Mixin类中不应该出现init初始化方法
- Mixin类通常不能独立工作,因为它是准备混入别的类中部分功能实现
- Mixin类的祖先类应是Mixin类
- 使用时Mixin类通常在继承列表的第一个位置
- Mixin类和装饰器:
- 这两种方式都可以使用,看个人选择
- 如果还需要继承则使用Mixin
- shape基类,要求所有子类都必须提供面积的计算,子类有三角形、矩形、圆。圆类的数据可序列化
- 面向对象实现LinkedList链表,单向实现append、iternodes,双向列表实现append、insert、remove、iternodes
本文来自投稿,不代表Linux运维部落立场,如若转载,请注明出处:http://www.178linux.com/88407