继承的应用,继承实现原理,super(),多继承的代码规范,组合
一、继承的应用
继承的应用:
对于你定义的每一个类,Python都会计算出一个方法解析顺序(MRO)列表,该MRO列表就是一个简单的所有基类的线性顺序列表,
MRO列表的构造是通过一个C3线性化算法来实现的,我们无需深究该算法的数学原理,它实际上就是合并所有父类的MRO列表,且在查找属性时,
Python会基于MRO列表按照从左到右的顺序依次查找基类,直到找到第一个匹配这个属性的类为止。
在Python中子类可以同时继承多个父类,在子类继承了多个父类时,经典类与新式类会有不同MRO,分别对应属性的两种查找方式:深度优先和广度优先
class Student:
school = "虹桥校区"
def __init__(self,name,age,gender):
self.name = name
self.age = age
self.gender = gender
def choose(self):
print("%s 选课成功" %self.name)
stu1 = Student("jack",18,"male")
stu2 = Student("tom",19,"male")
stu3 = Student('lili',29,"female")
class Teacher:
school = "虹桥校区"
def __init__(self,name,age,gender,level):
self.name = name
self.age = age
self.gender = gender
self.level = level
def score(self):
print("%s 正在为学生打分" %self.name)
tea1 = Teacher('egon',18,"male",10)
tea2 = Teacher('lxx',38,"male",3)
在子类派生的新方法中重用父类的功能:
方式一: 指名道姓地引用某一个类的函数,与继承无关
子类可以派生出自己新的属性,在进行属性查找时,子类中的属性名会优先于父类被查找,例如每个老师还有职称这一属性,我们就需要在Teacher类中定义该类自己的--init--覆盖父类的
class People:
school = "虹桥校区"
def __init__(self,name,age,gender):
self.name = name
self.age = age
self.gender = gender
class Student(People):
def choose(self):
print("%s 选课成功" %self.name)
class Teacher(People):
# 空对象,'egon',18,"male",10
def __init__(self,name,age,gender,level):
People.__init__(self,name,age,gender)
self.level = level
def score(self):
print("%s 正在为学生打分" %self.name)
stu1 = Student("jack",18,"male")
stu2 = Student("tom",19,"male")
stu3 = Student('lili',29,"female")
tea1 = Teacher('egon',18,"male",10) # 空对象,'egon',18,"male",10
tea2 = Teacher('lxx',38,"male",3)
# print(stu1.school)
# print(stu1.name)
# print(stu1.age)
# print(stu1.gender)
# print(tea1.__dict__)
二、继承实现原理
1 继承顺序
在Java和C#中子类只能继承一个父类,而Python中子类可以同时继承多个父类,如A(B,C,D)
如果继承关系为非菱形结构,则会按照先找B这一条分支,然后再找C这一条分支,最后找D这一条分支的顺序直到找到我们想要的属性
如果继承关系为菱形结构,那么属性的查找方式有两种,分别是:深度优先和广度优先
class A(object):
def test(self):
print('from A')
class B(A):
def test(self):
print('from B')
class C(A):
def test(self):
print('from C')
class D(B):
def test(self):
print('from D')
class E(C):
def test(self):
print('from E')
class F(D,E):
# def test(self):
# print('from F')
pass
f1=F()
f1.test()
print(F.__mro__) #只有新式才有这个属性可以查看线性列表,经典类没有这个属性
#新式类继承顺序:F->D->B->E->C->A
#经典类继承顺序:F->D->B->A->E->C
#python3中统一都是新式类
#pyhon2中才分新式类与经典类
继承顺序
2 继承原理(python如何实现的继承)
python到底是如何实现继承的,对于你定义的每一个类,python会计算出一个方法解析顺序(MRO)列表,这个MRO列表就是一个简单的所有基类的线性顺序列表,例如
>>> F.mro() #等同于F.__mro__
[<class '__main__.F'>, <class '__main__.D'>, <class '__main__.B'>, <class '__main__.E'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>]
为了实现继承,python会在MRO列表上从左到右开始查找基类,直到找到第一个匹配这个属性的类为止。
而这个MRO列表的构造是通过一个C3线性化算法来实现的。我们不去深究这个算法的数学原理,它实际上就是合并所有父类的MRO列表并遵循如下三条准则:
1.子类会先于父类被检查
2.多个父类会根据它们在列表中的顺序被检查
3.如果对下一个类存在两个合法的选择,选择第一个父类
三、super()
调用super()会得到一个特殊的对象,该对象专门用来引用父类的属性,且严格按照MRO规定的顺序向后查找
class People:
school = "虹桥校区"
def __init__(self,name,age,gender):
self.name = name
self.age = age
self.gender = gender
class Teacher(People):
# 空对象,'egon',18,"male",10
def __init__(self,name,age,gender,level):
# People.__init__(self,name,age,gender)
super(Teacher,self).__init__(name,age,gender)
self.level = level
def score(self):
print("%s 正在为学生打分" %self.name)
tea1 = Teacher('egon',18,"male",10) # 空对象,'egon',18,"male",10
print(tea1.__dict__)
super()是依赖于继承的,并且即使没有直接继承关系,super()仍然会按照MRO继续往后查找
super()案例:
>>> #A没有继承B
... class A:
... def test(self):
... super().test()
...
>>> class B:
... def test(self):
... print('from B')
...
>>> class C(A,B):
... pass
...
>>> C.mro() # 在代码层面A并不是B的子类,但从MRO列表来看,属性查找时,就是按照顺序C->A->B->object,B就相当于A的“父类”
[<class '__main__.C'>, <class '__main__.A'>, <class '__main__.B'>,<class ‘object'>]
>>> obj=C()
>>> obj.test() # 属性查找的发起者是类C的对象obj,所以中途发生的属性查找都是参照C.mro()
from B
四、多继承的代码规范
Pyton Mixins机制
一个子类可以同时继承多个父类,这样的设计常被人诟病,一来它有可能导致可恶的菱形问题,二来在人的世界观里继承应该是个”is-a”关系。 比如轿车类之所以可以继承交通工具类,是因为基于人的世界观,我们可以说:轿车是一个(“is-a”)交通工具,而在人的世界观里,一个物品不可能是多种不同的东西,因此多重继承在人的世界观里是说不通的,它仅仅只是代码层面的逻辑。不过有没有这种情况,一个类的确是需要继承多个类呢?
答案是有,我们还是拿交通工具来举例子:
民航飞机、直升飞机、轿车都是一个(is-a)交通工具,前两者都有一个功能是飞行fly,但是轿车没有,所以如下所示我们把飞行功能放到交通工具这个父类中是不合理的,加上mixin飞的功能:
class Vehicle:
pass
class FlyableMixin: #加上飞行功能
def fly(self):
print('flying')
class CivilAircraft(FlyableMixin,Vehicle): #民航飞机,加 飞的功能放在父类的左边
pass
class Helicopter(FlyableMixin,Vehicle): # 直升飞机,加上飞的功能
pass
class Car(Vehicle): #汽车
pass
五、组合
在一个类中以另外一个类的对象作为数据属性,称为类的组合。组合与继承都是用来解决代码的重用性问题。不同的是:继承是一种“是”的关系,比如老师是人、学生是人,当类之间有很多相同的之处,应该使用继承;而组合则是一种“有”的关系,比如老师有生日,老师有多门课程,当类之间有显著不同,并且较小的类是较大的类所需要的组件时,应该使用组合
简述--组合: 一个对象的属性值是指向另外一个类的对象
class People:
school = "虹桥校区"
def __init__(self,name,age,gender):
self.name = name
self.age = age
self.gender = gender
class Student(People):
def choose(self):
print("%s 选课成功" %self.name)
class Teacher(People):
# 空对象,'egon',18,"male",10
def __init__(self,name,age,gender,level):
People.__init__(self,name,age,gender)
self.level = level
def score(self):
print("%s 正在为学生打分" %self.name)
class Course:
def __init__(self,name,price,period):
self.name = name
self.price = price
self.period =period
def tell(self):
print('课程信息<%s:%s:%s>' %(self.name,self.price,self.period))
python = Course("python全栈开发",19800,"6mons")
linux = Course("linux",19000,"5mons")
stu1 = Student("jack",18,"male")
stu2 = Student("tom",19,"male")
stu3 = Student('lili',29,"female")
tea1 = Teacher('egon',18,"male",10) # 空对象,'egon',18,"male",10
tea2 = Teacher('lxx',38,"male",3)
stu1.courses = []
stu1.courses.append(python)
stu1.courses.append(linux)
print(stu1.courses)
for course_obj in stu1.courses:
course_obj.tell()