Day 20 继承/派生/菱形继承/新式类和经典类
类的继承
什么是继承
继承就是新建类的方法,新建的类被称为子类或者派生类
子类继承的类叫做父类,也称之为基类或者超类
继承的特征
子类可以继承父类的属性(特征与技能),并且可以派生出自己的属性(特征与技能)
注意:在python中子类可以同事继承多个父类,其他语言只能一个子类继承一个父类
为什么要继承
继承的目的是为了减少代码的冗余(减少代码重复)
如何实现继承
- 首先要确定谁是子类,谁是父类
- 在定义类时,子类+(), ()内写父类,实现继承
# 父类
class ParentClass1():
pass
class ParentClass2():
pass
# 子类
class ChildClass1(ParentClass1):
pass
# 继承多个父类
class ChildClass2(ParentClass1, ParentClass2):
pass
# __bases__是类的属性,用来查看子类的父类
print(ChildClass1.__bases__)
print(ChildClass2.__bases__)
(<class '__main__.ParentClass1'>,)
(<class '__main__.ParentClass1'>, <class '__main__.ParentClass2'>)
继承的关系
继承与抽象
继承描述的是子类和父类之间的关系,是一种什么是什么的关系.要找出这种关系,必须先抽象再继承,抽象即抽取类似或者比较像的部分.
抽象分成两个层次:
- 将对象间比较像的部分抽取成类
- 将类间比较像的部分抽取成父类
抽象最重要的作用是划分类别
# 选课系统
class OldboyTeacher():
def __init__(self, name, age, gender):
self.name = name
self.age = age
self.gender = gender
def change_score(self):
print('change_score')
class OldboyStudent():
def __init__(self, name, age, gender):
self.name = name
self.age = age
self.gender = gender
def choose_course(self, course):
print('{self.name}选择{course}课程')
两个类具有相同的__init__
,所以我们可以抽取出一个父类
# 选课系统
class OldboyPeople:
def __init__(self, name, age, gender):
self.name = name
self.age = age
self.gender = gender
class OldboyTeacher(OldboyPeople):
def change_score(self):
print('change_score')
class OldboyStudent(OldboyPeople):
def choose_course(self, course):
print('{self.name}选择{course}课程')
stu1 = OldboyStudent('Tiny', 18, 'male')
tea1 = OldboyTeacher('nick', 20, 'male')
print(stu1.name, stu1.age, stu1.gender)
print(tea1.name, tea1.age, tea1.gender)
Tiny 18 male
nick 20 male
属性查找顺序
class Foo:
def f1():
print('Foo.f1')
def f2():
print('Foo.f2')
self.f1()
class Soo(Foo):
def f1():
print('Soo.f1')
soo = Soo()
soo.f2()
Foo.f2
Soo.f1
对象查找属性的顺序:对象自己 ---> 对象的类 ---> 父类 ---> 父类 ---> ... ---> 报错
类的派生
派生:
子类中定义新的属性(特征和技能)的过程叫做派生
子类派生出新的属性,若与父类的属性相同,则以子类的为准
两种派生方式
方式一(类调用):
# 选课系统
class OldboyPeople:
def __init__(self, name, age, gender):
self.name = name
self.age = age
self.gender = gender
class OldboyTeacher(OldboyPeople):
def __init__(self, name, age, gender, salary):
OldboyPeople.__init__(self, name, age, gender)
self.salary = salary
def change_score(self):
print('change_score')
class OldboyStudent(OldboyPeople):
def choose_course(self, course):
print('{self.name}选择{course}课程')
tea1 = OldboyTeacher('nick', 20, 'male', 20000)
print(tea1.name, tea1.age, tea1.gender, tea1.salary)
方式二(super):
# 选课系统
class OldboyPeople:
def __init__(self, name, age, gender):
self.name = name
self.age = age
self.gender = gender
class OldboyTeacher(OldboyPeople):
def __init__(self, name, age, gender, salary):
super().__init__(name, age, gender)
self.salary = salary
def change_score(self):
print('change_score')
class OldboyStudent(OldboyPeople):
def choose_course(self, course):
print('{self.name}选择{course}课程')
tea1 = OldboyTeacher('nick', 20, 'male', 20000)
print(tea1.name, tea1.age, tea1.gender, tea1.salary)
新式类和经典类
- 新式类
- 继承了object的类以及该类的子类,都是新式类
- python3中的所有类都是新式类
- 经典类
- 没有继承object的类以及该类的子类,都叫做经典类
- 只有python2中才有经典类
菱形继承
由于在python中允许同时继承多个父类,如A(B, C, D)
如果继承关系为非菱形结构,则会按照先找B这条分支,然后再找C这条分支,最后找D这条分支直到找到我们需要的属性
如果继承关系为菱形结构,即子类的父类最后继承了同一个类,那么属性的查找方式有两种:
-
经典类:深度优先
-
新式类:广度优先
经典类:
新式类:
mro()方法
python到底是如何实现继承的,对于你定义的每一个类,python会计算出一个方法解析顺序(MRO)列表,这个MRO列表就是一个简单的所有基类的线性顺序列表,如:
class G:
pass
class F(G):
pass
class C(F):
pass
class E(G):
pass
class B(E):
pass
class A(B, C, D):
pass
print(A.mro())
class D(G):
pass
class F(G):
pass
class C(F):
pass
class E(G):
pass
class B(E):
pass
class A(B, C, D):
pass
print(A.mro())
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'>]
<class '__main__.A'>
<class '__main__.B'>
<class '__main__.E'>
<class '__main__.C'>
<class '__main__.F'>
<class '__main__.D'>
<class '__main__.G'>
<class 'object'>