继承
什么是继承:
继承是一种创建新类的方式,新建的类可以继承一个或多个父类(python支持多继承), 父类又可以称之为基类或超类,新建的类称为派生类或子类.
子类会’遗传’父类的属性,从而解决代码重用问题
为什么要用继承
减少类与类之间的代码冗余的问题
- python中类的继承分为: 单继承和多继承
class ParentClass1: #定义父类
pass
class ParentClass2: #定义另一个父类
pass
class SubClass1(ParentClass1): # 单继承,基类是ParentClass1,派生类是SubClass1
pass
class SubClass2(ParentClass1,ParentClass2): # python支持多继承,用逗号分隔开多个继承的类
pass
继承与抽象(先抽象再继承)
如何使用继承
继承描述的是子类与父类之间的关系, 是一种什么是什么的关系. 要找出这种关系,必须先抽象再继承
-
抽象:即抽取类似或者说比较像的部分.
抽象分成两个层次:- 1.将梅西和奥巴马这两个对象比较像的部分抽取成类;
- 2.将人,狗,猪这三类比较像的部分抽取称父类.
抽象最主要的作用是划分类别(可以隔离关注点, 降低复杂度)
-
继承:是基于抽象的结果,通过编程语言去实现它, 肯定是先经历抽象这个过程,才能通过继承的方式去表达出抽象的结构.
抽象只是分析和设计的过程中,一个技巧.通过抽象可以得到类
-
python2与python3在继承上的区别
新式类: 但凡继承object类的子类,以及该子类的子子类,…都称之为新式类
经典类:没有继承object类的子类,以及子类的子子类,…都称之为经典类
只有python2中才区分新式类与经典类,python3自动生成父类object,都是新式类,现在也都用新式类
继承的背景下属性查找的顺序
- 在单继承的背景下:无论是新式类还是经典类属性查找顺序都一样
- 先obj(对象)–>类–>父类…
class C:
xxx='C' # 如果对象obj和类A/类B都没有属性'xxx'会找到B的父类C的'xxx'
pass
class B(C):
xxx='B' # 如果对象obj和类A都没有属性'xxx'会找到A的父类B的'xxx'
pass
class A(B):
xxx='A' # 如果对象obj没有属性'xxx'会找到类A的'xxx'
pass
obj=A()
obj.xxx=111
print(obj.xxx) # obj--> A--> B--> C
- 在多继承背景下:如果一个子类继承了多个分支,但是多个分支没有汇聚到一个非object类,无论是新式类还是经典类属性查找顺序都一样:
- 会按照从左到右的顺序一个分支一个分支的查找下去
class E:
# xxx='E'
pass
class F:
# xxx='F'
pass
class B(E):
# xxx='B'
pass
class C(F):
# xxx='C'
pass
class D:
# xxx='D'
pass
class A(B,C,D):
# xxx='A'
pass
obj=A()
# obj.xxx=111
print(obj.xxx) # 查找顺序是obj-> A-> B-> E-> C-> F-> D->object
- 在多继承背景下,如果一个子类继承了多个分支,但是多个分支最终汇聚到一个非object类(菱形继承问题)
- 新式类:广度优先查找:obj->A->B->E->C->F->D->G->object
- 经典类:深度优先查找:obj->A->B->E->G->C->F->D
class G:
xxx='G'
class E(G):
xxx='E'
pass
class F(G):
xxx='F'
pass
class B(E):
xxx='B'
pass
class C(F):
xxx='C'
pass
class D(G):
xxx='D'
pass
class A(B,C,D):
xxx='A'
pass
print(A.mro) #可以查看mro列表
这样会有一个疑问,为什么要按照这样的问题查找,其实这是python给我们内置的方法,class之后会根据算法得到一个mro列表,属性查找顺序是按照mro列表从左到右来查找的.你运行一下上面的print(A.mro)
我们上面说的广度优先\深度优先都是用来便于理解的
class A:
def test(self):
print('A.test')
super().test()
class B:
def test(self):
print('from B')
class C(A,B):
pass
c=C()
c.test()
print(C.mro())
'''
这个的运行结果是:
A.test
from B
[<class '__main__.C'>, <class '__main__.A'>, <class '__main__.B'>, <class 'object'>]
A没有继承B 还是访问到了B,因为他是根据C的mro列表的顺序来的
'''
派生
子类也可以添加自己新的属性或者在自己这里重新定义这些属性(不会影响到父类),根据上面的属性查找顺序,一旦重新定义了自己的属性且与父类重名,那么调用新增的属性时,就以自己为准了。
我们说继承的用途是减少类和类之间的代码冗余的问题,所以我们通过继承父类来实现访问父类属于功能的需求.
- 在子类派生出的新功能中如何重用父类的功能:
- 方式一: 知名道姓地访问某一个类中的函数,(这种方法是不是继承都可以调用)
# 我们用选课系统来举例
# 先抽象再继承 的方式,这里不写分析过程
# 因为两个类有相同的部分,所以定义一个共同的父类
class OldboyPeople:
school = 'Oldboy'
def __init__(self,name,age,gender):
self.name = name
self.age = age
self.gender = gender
# 定义一个学生类,继承了OldboyPeople父类
class OldboyStudent(OldboyPeople):
def choose_course(self):
print('%s is choose course' %self.name)
# 定义一个教师类,继承了OldboyPeople父类
class OldboyTeacher(OldboyPeople):
# 派生新的功能
def __init__(self,name,age,gender,level,salary):
# 调用父类的功能,用指名道姓的方式, 类名.函数(参数)
OldboyPeople.__init__(self,name,age,gender)
# 派生的init功能
self.level = level
self.salary = salary
def score(self,stu,num):
stu.num = num
print('老师%s给学生%s打分%s' %(self.name,stu.name,num))
stu = OldboyStudent('wood',18,'male')
tea = OldboyTeacher('egon',18,'male',10,30000)
# print(tea.__dict__)
tea.score(stu,90)
- 方式二:super(OldboyTeacher,self),在python3中super可以不传参数,调用该函数会得到一个特殊的对象,该对象时专门用来访问父类中的属性.
强调:super会严格按照属性的查找方式,也就是参照对象self所在类的mro列表依次查找属性
class OldboyPeople:
school = 'Oldboy'
def __init__(self,name,age,gender):
self.name = name
self.age = age
self.gender = gender
class OldboyStudent(OldboyPeople):
def choose_course(self):
print('%s is choose course' %self.name)
class OldboyTeacher(OldboyPeople):
def __init__(self,name,age,gender,level,salary):
# 用super函数重用父类的功能,python3可以不传参
# super(OldboyPeople,self).__init__(name,age,gender)
super().__init__(name,age,gender)
self.level = level
self.salary = salary
def score(self,stu,num):
stu.num = num
print('老师%s给学生%s打分%s' %(self.name,stu.name,num))
stu = OldboyStudent('wood',18,'male')
tea = OldboyTeacher('egon',18,'male',10,30000)
# print(tea.__dict__)
tea.score(stu,90)