一、继承介绍
继承
继承是一种新建类的方式,新建的类称之为子类,被继承的类称为父类,也称为基类与超类
为何要用继承:
子类会遗传父类的属性(与方法),所以继承是解决类与类之间代码冗余的问题
还记得我们为什么会定义出类吗,我们学习的编程方法叫做面向对象编程,而不是面向类编程,那前几天开始学习时我们的类是为什么创建出来的呢?
当我们创建对象时,发现很多对象都会有相同的特征,如名字,性别,年龄等,每创建一个对象我们就要写重复代码,所以类是为了减少对象与对象之间代码冗余的问题
如何继承
class Parent1:
pass
class Parent2:
pass
class Sub1(Parent1):
pass
class Sub2(Parent1,Parent2):
pass
print(Sub1.__bases__) #利用__bases__可以查看一个类继承了哪些类
print(Sub2.__bases__)
----------------------
(<class '__main__.Parent1'>,)
(<class '__main__.Parent1'>, <class '__main__.Parent2'>)
# 继承案列:
class MIT:
school = "MIT"
class Student(MIT):
def __init__(self,name,age,gender,stud_id,course):
self.name = name
self.age = age
self.gender = gender
self.stu_id = stud_id
self.course = course
def choose(self):
print('%s 正在选课' %self.name)
stu1=Student("yang",18,'male',1001,"python")
print(stu1.school)
-----------------
MIT
二、继承与抽象
名字很高大上,其实就是为了帮助我们理解为什么要有父类。抽象的意思,就是我们从不同的对象总结相同特点,创建一个类,之后再根据类的相同特征再找出一个父类,如人可以是一个类,猫可以是一个类,狗可以是一个类,我们还可以根据这三个类总结一个动物类。这个过程就是抽象的过程。
而当我们根据以上的关系根据三个类实例化出对象,那么动物类就是人,猫,狗的父类。
三、属性查找
有了继承关系,对象在查找属性时,先从对象自己的__dict__中找,如果没有则去自己的类中找,然后再去父类中找。
因为属性查找的顺序,所以当子类与父类都有同一个方法名(但是功能不同)时,总是会优先查找子类中的方法,就产生了一个覆盖的效果。
若父类不想让子类调用自己的方法,可以利用隐藏属性,加入双下划线,这就表示这个方法只能在本类中调用
# 在子类派生的新方法中重用父类的功能
# 方式一:指名道姓地调用某一个类的函数
# 特点:不依赖于继承关系
class MITPeople:
school = "MIT"
def __init__(self,name,age,gender):
self.name = name
self.age = age
self.gender = gender
def f1(self):
print('11111')
class Student(MITPeople):
def __init__(self,name,age,gender,stu_id,course):
MITPeople.__init__(self,name,age,gender)
self.stu_id = stu_id
self.course = course
def choose(self):
print('%s 正在选课' %self.name)
def f1(self):
MITPeople.f1(self)
print("22222")
class Teacher(MITPeople):
def score(self,stu,num):
stu.num = num
stu1=Student("yang",18,'male',1001,"python")
stu1.f1()
-----------------
11111
22222
四、继承的实现原理
新式类:继承了Object类的子类以及他的所有子类,都称为新式类
经典类:没有继承Object类的任何类都称为经典类
在python3中,做了处理,所有的类都是新式类。即如果你在定义一个类的时候,没有继承其他类,python3会默认帮你继承Object类,在python2中并无此处理,所以python2中既有经典类也有新式类。
若你写的类想要兼容python2与python3,只要在定义类时,继承Object就可以了
五、菱形问题
菱形问题:一个子类继承的多条分支最终汇聚到一个不是Object的类,注意,菱形问题不是一个好事情,在你的项目中一定要尽可能的避免这个问题
如果继承关系为菱形结构,即子类的父类最后继承了同一个类且这个类不是object,那么属性的查找方式有两种:
经典类:深度优先
新式类:广度优先
深度优先演示图:
广度优先演示图:
C3算法与mro()方法介绍(了解即可)
python到底是如何实现继承的,对于你定义的每一个类,python会计算出一个方法解析顺序(MRO)列表,这个MRO列表就是一个简单的所有基类的线性顺序列表,如:
print(A.mro()) # A.__mro__
---------------------------
[<class '__main__.A'>, <class '__main__.B'>, <class '__main__.E'>, <class '__main__.C'>, <class '__main__.F'>, <class '__main__.D'>, <class '__main__.G'>, <class 'object'>]
for i in A.mro():
print(i)
---------------------------
<class '__main__.A'>
<class '__main__.B'>
<class '__main__.E'>
<class '__main__.C'>
<class '__main__.F'>
<class '__main__.D'>
<class '__main__.G'>
<class 'object'>
六、Pyton Mixins机制
名字很高级,说白了就是把只属于自己的类方法从大的类里提取出来,然后多继承。通过下面的例子很容易看懂。
首先这不是一个语法,而更像是一个python程序员之间的约定
Python既然设计了可以多继承,那么多继承一定有他便利的地方,最了然的就是我们可以继承多个类,减少代码冗余。但是多继承又容易产生菱形问题,并且和我们以往的继承思想有所不同。
人的世界观里继承应该是个”is-a”关系。 比如轿车类之所以可以继承交通工具类,是因为基于人的世界观,我们可以说:轿车是一个(“is-a”)交通工具,而在人的世界观里,一个物品不可能是多种不同的东西,因此多重继承在人的世界观里是说不通的。
但是在我们的程序设计中,又是可能会出现一个对象却是需要继承多个类。如下例子:
'''
民航飞机、直升飞机、轿车都是一个(is-a)交通工具,前两者都有一个功能
是飞行fly,但是轿车没有,所以如下所示我们把飞行功能放到交通工具这个
父类中是不合理的
'''
class Vehicle: # 交通工具
def fly(self):
# 飞行功能相应的代码
print("I am flying")
class CivilAircraft(Vehicle): # 民航飞机
pass
class Helicopter(Vehicle): # 直升飞机
pass
class Car(Vehicle): # 汽车并不会飞,但按照上述继承关系,汽车也能飞了
pass
但是如果民航飞机和直升机都各自写自己的飞行fly方法,又违背了代码尽可能重用的原则(如果以后飞行工具越来越多,那会重复代码将会越来越多)。
怎么办???为了尽可能地重用代码,那就只好在定义出一个飞行器的类,然后让民航飞机和直升飞机同时继承交通工具以及飞行器两个父类,这样就出现了多重继承。这时又违背了继承必须是”is-a”关系。这个难题该怎么解决?
Python提供了Mixins机制,简单来说Mixins机制指的是子类混合(mixin)不同类的功能,而这些类采用统一的命名规范(例如Mixin后缀),以此标识这些类只是用来混合功能的,并不是用来标识子类的从属"is-a"关系的,所以Mixins机制本质仍是多继承,但同样遵守”is-a”关系,如下
class Vehicle: # 交通工具
pass
class FlyableMixin:
def fly(self):
'''
飞行功能相应的代码
'''
print("I am flying")
class CivilAircraft(FlyableMixin, Vehicle): # 民航飞机
pass
class Helicopter(FlyableMixin, Vehicle): # 直升飞机
pass
class Car(Vehicle): # 汽车
pass
# ps: 采用某种规范(如命名规范)来解决具体的问题是python惯用的套路
使用Mixin类实现多重继承要非常小心
首先它必须表示某一种功能,而不是某个物品,python 对于mixin类的命名方式一般以 Mixin, able, ible 为后缀
其次它必须责任单一,如果有多个功能,那就写多个Mixin类,一个类可以继承多个Mixin,为了保证遵循继承的“is-a”原则,只能继承一个标识其归属含义的父类
然后,它不依赖于子类的实现
最后,子类即便没有继承这个Mixin类,也照样可以工作,就是缺少了某个功能。(比如飞机照样可以载客,就是不能飞了)