面向对象之继承
一、组合
1、定义:自定义类的对象作为类的属性
A类的对象具备某一个属性,该属性的值是B类的对象
基于这种方式就把A类与B类组合到一起
对象既能使用A类中的数据与功能,也能使用B类中的数据与功能
2、作用:组合与继承的作用一样,都是用来减少类与类之间的重复代码
class Teacher: def __init__(self, name, age): self.name = name self.age = age t1 = Teacher('Owen', 17) print(t1.name, t1.age) # Owen 17 print(type(t1.name), type(t1.age)) # <class 'str'> <class 'int'> class Student: # 学生可以有老师属性 def __init__(self, name, age, teacher): self.name = name self.age = age # 自定义类的对象作为类的属性 self.teacher = teacher stu = Student('Bob',18,t1 ) # print(stu.__dict__) # {'name': 'Bob', 'age': 18, 'teacher': <__main__.Teacher object at 0x00000000021F7E10>} # 学生的年龄和姓名 print(stu.name, stu.age) # Bob 18 # 学生的老师年龄和姓名 print(stu.teacher.name, stu.teacher.age) # Owen 17
二、继承
1、定义:一种关系,子类可以通过父类获取属性和方法
继承是一种新建类的方式,新建的类称为子类或派生类
父类又称为基类、超类
作用:子类可以“遗传”父类的属性,从而可以减少代码冗余
如何寻找继承关系:先抽象,再继承,继承描述的就是 一种父子关系/从属关系
2、继承语法:
class Foo1: pass class Bar(Foo1): pass class Bar: # 在python3中没有继承任何类的类,默认继承object pass
例: class People: def __init__(self, name): self.name = name def eat(self): print(self.name + '在吃饭') class Student(People): identify = '学生' # def __init__(self, name): # self.name = name # def eat(self): # print(self.name + '在吃饭') student = Student('Bob') student.eat() # Bob在吃饭 class Leader(People): # def __init__(self, name): # self.name = name # # def eat(self): # print(self.name + '在吃饭') pass leader = Leader('Yang') leader.eat() # Yang在吃饭
3、继承关系(继承规则):继承是类与类之间的关系,寻找这种关系需要先抽象再继承
①父类的所有未封装的属性和方法,子类都能访问
②父类的所有封装的属性和方法,子类都不能访问
在外界通过子类或子类对象,不能访问
在子类内部也不能访问
class OldboyPeople: school = 'oldboy' def __init__(self, name, age, sex): self.name = name self.age = age self.sex = sex class OldboyTeacher(OldboyPeople): def change_score(self): print('teacher %s is changing score' % self.name) class Oldboystudent(OldboyPeople): def choose(self): print('student %s choose course' % self.name) tea1 = OldboyTeacher('egon', 18, 'male') stu1 = Oldboystudent('alex', 73, 'female') print(tea1.name, tea1.age, tea1.sex) # egon 18 male
4、继承关系下的属性查找顺序:
①优先找自身,自身没有找父类
②父类没有找父类的父类
③一直找到最顶级的父类,如果还没有报错
5、抽离和派生
抽离:先有多个共有的类,抽出共性形成父类
派生:子类定义的名字会覆盖父类的同名属性
三、方法重写和重用
1、重写:先写好父类的方法,由于父类方法的功能不满足子类需求
子类可以重写父类方法:方法名与父类相同,自定义方法的实现体
2、重用:还需要父类方法的功能,在父类方法功能基础上再添加新功能
突破点:在子类中去调用父类的方法,还有保证调用者是子类(子类的对象)
# 1、方法的重写 class Sup: num = 10 def test(self): print('test sup') class Sub(Sup): num = 100 # 先写好父类的方法,由于父类方法的功能不满足子类需求, # 子类可以重写父类方法:方法名与父类相同,自定义方法的实现体 def test(self): print('test sub') print(Sub.num) # 100 Sub().test() # test sub # 2、方法的重用 class Sup: def test(self): print('test sup') class Sub(Sup): pass # 重用:还需要父类方法的功能,在父类方法功能基础上再添加新功能 # 突破点:在子类中去调用父类的方法,还有保证调用者是子类(子类的对象) def test(self): # python2中写法:super(Sub, self).test() # python3中简化写法 super().test() print('test sub') Sub().test() # test sub
四、init结合super()
super关键字
super()可以得到调用父级功能的对象,调用者还是子类对象
super()只能在子类的方法中使用
uper()本质super(子类类名,当前对象)
# 人类:只需要初始化 - name # 老师: 要初始化 - name salary # 学生: 要初始化 - name grade class Sup: def test(self): print(self) def __init__(self, name): self.name = name class Sub(Sup): # 有继承关系下,只要名字相同,即使产生不同,还是属于同一个方法 def test(self, num): super().test() print(num) # 默认父级的__init__可以被继承过来, # 但是会出现子类对象的属性比父类多 def __init__(self, name, salary): super().__init__(name) # 父级有的共性功能通过super()交给父级做 self.salary = salary # 子类特有的自己来完成 # Sub().test(10) # Sub().test() # 使用还是使用自身带参的,不能使用父级不带参的 # (本质名字相同就是一个,优先查找自己的)
五、多继承
1、简单的多继承
属性的查找顺序:优先找自己的,如果没有,按照继承先后查找父级
class A: name = 'A' num = 10 class B: name = 'B' count = 100 # 子类可以继承所有父类的所有可继承属性 class C(A, B): # 自己 => A => B pass print(C.num) # 10 print(C.count) # 100 print(C.name) # A # 打印属性查找的顺序 print(C.mro()) # [<class '__main__.C'>, <class '__main__.A'>, <class '__main__.B'>, <class 'object'>]
2、复杂的多继承
class A: name = "A" class B(A): name = "B" class C: name = "C" class D(C): name = "D" class E(B, D): name = "E" print(E.mro()) # [<class '__main__.E'>, <class '__main__.B'>, <class '__main__.A'>, <class '__main__.D'>, <class '__main__.C'>, <class 'object'>]
3、新式类与经典类
①新式类:继承object的类及其子类都是新式类
在python3中如果一类没有继承任何父类,那么默认继承object类,即在python3中所有的类都是新式类
②经典类:(只在python2中才有)没有继承object的类及其子类都是经典类
③二者的区分:在菱形继承下
经典类:深度优先查找
新式类:广度优先查找
class Foo(object): pass class Bar(Foo): pass print(Foo.__bases__) # (<class 'object'>,) print(Bar.__bases__) # (<class '__main__.Foo'>,)