类的继承、派生、组合
文章目录
- 类的继承、派生、组合
- 一、类的继承
- 二、派生
- 三、组合
一、类的继承
继承是一种新建类的方式,新建的类称为子类,被继承的类称为父类。
继承的特性是:子类会遗传父类的属性(继承是类与类之间的关系)。
继承的好处就是可以减少代码的冗余。
在python中支持一个类同时继承多个父类。python3中如果一个类没有继承任何类,那默认继承object类,在python2中如果一个类没有继承任何类,不会继承object类。
新式类:但凡继承了object的类以及该类的子类都是新式类。
经典类:没有继承object的类以及该类的子类都是经典类。
在python3中都是新式类,只有在python2中才区别新式类与经典类。
新式类可以通过内置的__str__方法修改实例的打印值。
类继承类时只需要才()中写上需要继承的类即可,可以通过类的__bases__属性查看该类继承的类。
class A():
pass
class B(A):
def __str__(self):
return 'from B'
print(B.__bases__)
b=B()
print(b)
<<< (<class '__main__.A'>,)
<<< from B
类的继承是为了减少代码的冗余:
class People:
school='五道口职业技术学院'
def __init__(self,name,age):
self.name=name
self.age=age
class Student(People):
pass
class Teacher(People):
pass
s=Student('张三',18)
t=Teacher('李四',40)
print(s.school,t.school)
print(s.name,t.name)
<<<五道口职业技术学院 五道口职业技术学院
<<<张三 李四
上述代码中Student和Teacher中没有__init__方法和school属性,初始化时就会取父类People中找。People作为Student和Teacher的父类,存储了__init__方法和school属性就避免了Student和Teacher中重复存储代码的问题。
菱形继承:当一个子继承多个父类时,多个父类最终继承了同一个类,称之为菱形继承
菱形继承的问题:
- 经典类下查找属性:深度优先查找
- 新式类下查找属性:广度优先查找
总结一下,类中属性查找,方法继承的方式为:实例、实例对应的类、父类
如果继承关系是非菱形的,类的继承会按直接继承的顺序先找完一条路,再去找其他的路。如图所示的继承顺序为:先找最左侧的路B、E,再找中间的路C,最后找右侧的路D、F。也可以通过mro表直接查看类的继承顺序。
class F():
pass
class E():
pass
class B(E):
pass
class C():
pass
class D(F):
pass
class A(B,C,D):
pass
print(A.__mro__)
<<< (<class '__main__.A'>, <class '__main__.B'>, <class '__main__.E'>, <class '__main__.C'>, <class '__main__.D'>, <class '__main__.F'>, <class 'object'>)
菱形继承的实现原理(新式类):图中的继承顺序为:首先按A的直接继承顺序找B这条路为B、E,到两条路相交的节点G后会跳转到下一条路C,再到下一条路D,找到D、F,当三条路全部找完以后再找节点G,之后再找F。
class H():
pass
class G(H):
pass
class F(G):
pass
class E(G):
pass
class B(E):
pass
class C():
pass
class D(F):
pass
class A(B,C,D):
pass
print(A.__mro__)
<<< (<class '__main__.A'>, <class '__main__.B'>, <class '__main__.E'>, <class '__main__.C'>, <class '__main__.D'>, <class '__main__.F'>, <class '__main__.G'>, <class '__main__.H'>, <class 'object'>)
由于一个类可以继承多个父类会导致复杂的菱形继承问题,所以python中设置了Mixins规范(阅读俗称的规范)
class Vehicle:
pass
class FlyableMixin:
"""
飞行功能
"""
print('flying')
class CivilAircraft(FlyableMixin,Vehicle):
pass
class Helicopter(FlyableMixin,Vehicle):
pass
如上代码中CivilAircraft、Helicopter表示两种类型的飞机,而飞机既属于交通工具又能飞行,为了避免复杂的多类继承问题,我们人为规定FlyableMixin表示一种功能混入CivilAircraft、Helicopter继承的类中用以提示代码阅读者FlyableMixin并不被视为父类,而Vehicle才是真正的父类。
Mixins规范注意点:
- mixins类必须表示某种功能混入继承的类中,且一般以mixin、able、ible为后缀
- mixins类表示的功能单一,如果需要混入多个功能必须写多个mixins类
- 子类即便没有继承mixins类依然可以工作,只是子类会缺少某个功能
- mixins类功能的实现不依赖继承它的子类
二、派生
派生:子类中新定义的属性,子类在使用时始终以自己的为准。
class People:
school='五道口职业技术学院'
def __init__(self,name,age):
self.name=name
self.age=age
class Student(People):
def __init__(self,name,age,class_name):
#People.__init__(self,name,age)
super(Student,self).__init__(name,age)
self.class_name=class_name
s=Student('张三',18,'一班')
print(s.name,s.age,s.class_name)
<<<张三 18 一班
上述代码中Student类派生了People的__init__方法,加入了class_name参数的初始化。由于name和age参数的初始化在People类中已经存在,为了避免代码的重复,可以重用父类的__init__方法。重用的方法有两种,一种是直接指名道姓使用People类的__init__方法,这种方式必须将self参数也传如__init__方法中;另一种是使用super函数继承People类的__init__方式,这种方式无需传入self参数,super会自动将函数中的self传给父类的__init__方法。在python3中super()中的参数可以不写,但是python2中super()中传的参数必须写上,并且必须是新式类才能使用super。
注意点:
- super函数会以调用super函数的类为起始按类的继承顺序向后寻找继承的方法。可以通过mro表查看super的继承顺序。
__mro__和__bases__的区别在于__bases__只能查看类直接的继承顺序,而__mro__可以查看类所以的继承顺序。
class F():
pass
class A(F):
def f1(self):
print('from A')
super().f1()
class B():
def f1(self):
print('from B')
class C(A,B):
def f1(self):
print('from C')
super().f1()
c=C()
print(C.__mro__)
print(C.__bases__)
<<< (<class '__main__.C'>, <class '__main__.A'>, <class '__main__.F'>, <class '__main__.B'>, <class 'object'>)
<<< (<class '__main__.A'>, <class '__main__.B'>)
c.f1()
<<< from C
<<< from A
<<< from B
上述代码中c.f1()首先会运行C中的f1代码,然后运行super().f1(),此刻调用super的类是C,mro表中以C为起始向后寻找继承关系,找到A类的f1函数,再次运行super函数,再次在mro表中以A为起始向后找,找到B类,运行B的f1函数。
三、组合
组合就是一个类的对象具备某一个属性,该属性的值是指向另外外一个类的对象。
组合也是用来解决类与类直接代码冗余问题的。
class Course():
def __init__(self,name,price):
self.name=name
self.price=price
class Teacher():
def __init__(self,name):
self.name=name
self.course=[]
python=Course('python',1000)
c=Course('c',500)
t1=Teacher('张1')
t2=Teacher('张2')
#将t1、t2的course属性和python、c组合到一起以便于使用course属性可以直接参考price属性的值
t1.course.extend([python,c])
t2.course.append(c)
print(t1.course[0].price)
print(t2.course[0].price)
<<<1000
<<<500