一、继承
1.什么是继承
继承是一种新建类的方式,新建的类称之为子类或派生类,继承的父类称之为基类或超类。
在python中,一个子类可以继承多个父类
在其他语言中,一个子类只能继承一个父类
2.继承的作用
减少代码的冗余
3.继承的实现
先确认谁是父类,先抽象,再继承;谁是子类
1 # 父类 2 class Father1: 3 x = 1 4 pass 5 6 class Father2: 7 pass 8 9 class Father3: 10 pass 11 12 13 # 子类 14 class Sub(Father1): 15 pass 16 17 # 子类查看父类__bases__ 18 print(Sub.__bases__) 19 # (<class '__main__.Father1'>,) 20 print(Sub.x) 21 # 1
1 # 问题: 代码冗余 2 # 老师类 3 class OldboyTeacher: 4 school = 'oldboy' 5 country = 'China' 6 7 def __init__(self, name, age, sex): 8 self.name = name 9 self.age = age 10 self.sex = sex 11 12 # 老师修改分数 13 def change_score(self): 14 print(f'老师 {self.name} 正在修改分数...') 15 16 17 # 学生类 18 class OldboyStudent: 19 school = 'oldboy' 20 country = 'China' 21 22 def __init__(self, name, age, sex): 23 self.name = name 24 self.age = age 25 self.sex = sex 26 27 # 学生选择课程 28 def choose_course(self): 29 print(f'学生 {self.name} 正在选择课程...') 30 31 32 stu1 = OldboyStudent('YJG', 50, 'female') 33 print(stu1.school, stu1.name, stu1.age, stu1.sex) 34 35 tea1 = OldboyTeacher('tank', 17, 'male') 36 print(tea1.school, tea1.name, tea1.age, tea1.sex)
解决代码冗余
1 class People: 2 school = 'oldboy' 3 country = 'China' 4 5 def __init__(self, name, age, sex): 6 self.name = name 7 self.age = age 8 self.sex = sex 9 10 class Student(People): 11 # school = 'oldboy' 12 # country = 'China' 13 14 # def __init__(self, name, age, sex): 15 # self.name = name 16 # self.age = age 17 # self.sex = sex 18 def func(self): 19 print('from student') 20 21 class Teacher(People): 22 # school = 'oldboy' 23 # country = 'China' 24 25 # def __init__(self, name, age, sex): 26 # self.name = name 27 # self.age = age 28 # self.sex = sex 29 def func(self): 30 print('from teacher') 31 32 33 s1 = Student('张三', 23, '男') 34 t1 = Teacher('李四', 25, '女') 35 36 print(s1.name, s1.age, s1.sex, s1.school) 37 print(t1.name, t1.age, t1.sex, t1.school)
4.继承的查找
注意:程序的执行顺序是由上到下,父类必须定义在子类的上方
在继承背景下,对象属性的查找顺序:
1.先从对象自己的名称空间中查找
2.对象中没有,从子类的名称空间中查找。
3.子类中没有, 从父类的名称空间中查找,若父类没有,则会报错!
1 # 父类 2 class Father: 3 x = 10 4 pass 5 # 子类 6 class Sub(Father): 7 x = 20 8 pass 9 sub_obj = Sub() 10 11 # 此方法并未修改调用对象类的值,而是给调用产生的对象增加一个属性 12 print('对象的名称空间:', sub_obj.__dict__) 13 sub_obj.x = 30 14 print(sub_obj.x) # 30 15 print('父类的名称空间:', Father.__dict__) 16 print('子类的名称空间:', Sub.__dict__) 17 print('对象的名称空间:', sub_obj.__dict__)
1 # 父类 2 class Father: 3 x = 10 4 pass 5 # 子类 6 class Sub(Father): 7 x = 20 8 pass 9 sub_obj = Sub() 10 # sub_obj.x = 30 11 print(sub_obj.x) # 20 12 13 14 # 父类 15 class Father: 16 x = 10 17 pass 18 # 子类 19 class Sub(Father): 20 # x = 20 21 pass 22 sub_obj = Sub() 23 # sub_obj.x = 30 24 print(sub_obj.x) # 10 25 26 27 # 父类 28 class Father: 29 # x = 10 30 pass 31 # 子类 32 class Sub(Father): 33 # x = 20 34 pass 35 sub_obj = Sub() 36 # sub_obj.x = 30 37 print(sub_obj.x) # 报错
二、派生
1.什么是派生
指的是子类继承父类的属性与方法,并且派生出自己独有的属性与方法。
若子类中的方法名与父类的相同,优先用子类的。
2.派生的实现
子类继承父类,派生出自己的属性与方法,并且重用父类的属性与方法。
1 # 父类 2 class Foo: 3 def f1(self): 4 print('from Foo.f1') 5 def f2(self): 6 print('from Foo.f2') 7 self.f1() 8 class Bar(Foo): 9 # 重写 10 def f1(self): 11 print('from Bar.f1') 12 13 def func(self): 14 print('from Bar.func') 15 bar_obj = Bar() 16 17 bar_obj.f1() 18 # from Bar.f1 19 bar_obj.func() 20 # from Bar.func 21 bar_obj.f2() 22 """ 23 输出结果: 24 from Foo.f2 25 from Bar.f1 26 """
问题:子类重写父类的__init__导致代码更加冗余
class OldboyPeople: school = 'oldboy' def __init__(self, name, age, sex): self.name = name self.age = age self.sex = sex class OldboyTeacher(OldboyPeople): def __init__(self, name, age, sex, sal): self.name = name self.age = age self.sex = sex self.sal = sal def change_score(self): print(f'老师 {self.name} 修改分数...') class OldboyStudent(OldboyPeople): def __init__(self, name, age, sex, girl): self.name = name self.age = age self.sex = sex self.girl = girl def choose_course(self): print(f'学生 {self.name} 选择课程...') tea1 = OldboyTeacher('张三', 23, 'male', 1500) stu1 = OldboyStudent('李四', 28, 'male', '凤姐') print(tea1.name, tea1.age, tea1.sex, tea1.sal) print(stu1.name, stu1.age, stu1.sex, stu1.girl)
解决问题: 子类重用父类的属性,并派生出新的属性。
两种方式:
1.直接引用父类的__init__为其传参,并添加子类的属性。
2.通过super来指向父类的属性。
- super()是一个特殊的类,调用super得到一个对象,该对象指向父类的名称空间。
注意: 使用哪一种都可以,但不能两种方式混合使用。
1 # 方法一: 2 class OldboyPeople: 3 school = 'oldboy' 4 5 def __init__(self, name, age, sex): 6 self.name = name 7 self.age = age 8 self.sex = sex 9 10 11 class OldboyTeacher(OldboyPeople): 12 13 def __init__(self, name, age, sex, sal): 14 OldboyPeople.__init__(self, name, age, sex) 15 self.sal = sal 16 17 # self.name = name 18 # self.age = age 19 # self.sex = sex 20 # self.sal = sal 21 def change_score(self): 22 print(f'老师 {self.name} 修改分数...') 23 24 25 class OldboyStudent(OldboyPeople): 26 27 def __init__(self, name, age, sex, girl): 28 OldboyPeople.__init__(self, name, age, sex) 29 self.girl = girl 30 # self.name = name 31 # self.age = age 32 # self.sex = sex 33 # self.girl = girl 34 35 def choose_course(self): 36 print(f'学生 {self.name} 选择课程...') 37 38 39 tea1 = OldboyTeacher('张三', 23, 'male', 1500) 40 stu1 = OldboyStudent('李四', 28, 'male', '凤姐') 41 print(tea1.name, tea1.age, tea1.sex, tea1.sal) 42 print(stu1.name, stu1.age, stu1.sex, stu1.girl)
1 # 方法二 2 class OldboyPeople: 3 school = 'oldboy' 4 5 def __init__(self, name, age, sex): 6 self.name = name 7 self.age = age 8 self.sex = sex 9 10 11 class OldboyTeacher(OldboyPeople): 12 13 def __init__(self, name, age, sex, sal): 14 super().__init__(name, age, sex) 15 # self.name = name 16 # self.age = age 17 # self.sex = sex 18 self.sal = sal 19 20 def change_score(self): 21 print(f'老师 {self.name} 修改分数...') 22 23 24 class OldboyStudent(OldboyPeople): 25 26 def __init__(self, name, age, sex, girl): 27 super().__init__(name, age, sex) 28 # self.name = name 29 # self.age = age 30 # self.sex = sex 31 self.girl = girl 32 33 def choose_course(self): 34 print(f'学生 {self.name} 选择课程...') 35 36 37 tea1 = OldboyTeacher('张三', 23, 'male', 1500) 38 stu1 = OldboyStudent('李四', 28, 'male', '凤姐') 39 print(tea1.name, tea1.age, tea1.sex, tea1.sal) 40 print(stu1.name, stu1.age, stu1.sex, stu1.girl)
3、新式类与经典类
- 新式类:
1.凡是继承object的类或子孙类都是新式类。
2.在python3中所有的类都默认继承object。
- 经典类:
1.在python2中才会有经典类与新式类之分。
2.在python2中,凡是没有继承object的类,都是经典类。
mro的查找顺序:
新式类:广度优先
经典类:深度优先
查找顺序可以从下图看出