【一】什么是继承
-
继承就是创建新类的一种方式,这个新类可以继承一个或者多个其他类的属性
-
新的类如果有自己属性,那就叫派生
【二】继承的优点
-
可以继承父类的所有属性和方法,这样就可以实现代码去重。
【三】继承方式
-
单继承:继承一个父类的子类
-
多继承:继承多个父类的子类
例如:
class Student(School): # 被继承的类叫父类 School # 新类就叫子类 Student
【四】单继承
class Person(object): height = 180 weight = 60 # 继承Person class Student(Person): def __init__(self, name): self.name = name def tell_me(self): print(f"我是{self.name},我的身高是{self.height},我的体重是{self.weight}") stu1 = Student("knight") stu1.tell_me()
【五】多继承
class Person(object): height = 180 weight = 60 class School(object): school_name = '家里蹲' # 继承两个父类,Person和School class Teacher(Person,School): def __init__(self, name): self.name = name def tell_me(self): print(f"我是{self.name},我所在的学校是{self.school_name},我的身高是{self.height},我的体重是{self.weight}") tea1 = Teacher("knight") tea1.tell_me()
【六】查看当前继承的父类状况
# base 只打印一个当前继承的父类,如果当前继承了多个,那么默认打印第一个 # base 打印所有当前继承的父类 print(Student.__base__) # 只继承了一个父类,只能查看到一个父类 print(Teacher.__base__) # 虽然继承了两个父类,但也只能查看到从左往右第一个父类 print(Student.__bases__) # 只继承了一个父类,可以查看所有继承的父类 print(Teacher.__bases__) # 继承了两个父类,也可以查看所有继承的父类
【七】经典类和新式类
# 经典类和新式类的区别在于Python版本的不同 # 在 py3 版本之前存在两个概念 ,在之后就没有经典类的概念了,只有新式类 # 【一】什么是经典类 # 在py2中没有显示继承 object 的类或者是该类的子类都是经典类 # 【二】什么是新式类 # 在py2中显示继承 object 的类或者是该类的子类都是新式类 # 在py3之后所有的类默认都是新式类,不写m默认继承 object
【八】继承和抽象
-
继承是由少变多
-
抽象是由多变少
# 【1】没有继承和抽象 class Cat(object): def speak(self): print(f"喵喵叫") def eat(self): print(f"猫可以吃饭") def drink(self): print(f"猫可以喝水") class Dog(object): def speak(self): print(f"汪汪叫") def eat(self): print(f"狗可以吃饭") def drink(self): print(f"狗可以喝水") cat_one = Cat() print(cat_one.speak()) dog_one = Dog() print(dog_one.speak())
# 【使用抽象和继承】 # 先抽象 class Animal(object): def speak(self): print(f"{self.name}可以叫") def eat(self): print(f"{self.name}可以吃饭") def drink(self): print(f"{self.name}可以喝水") # 再继承 class Cat(Animal): def __init__(self,name): self.name = '猫'+name class Dog(Animal): def __init__(self,name): self.name = '狗'+name cat_one = Cat(name='小花') print(cat_one.speak()) # 猫小花可以叫 dog_one = Dog(name='小黑') print(dog_one.eat()) # 狗小黑可以吃饭
【九】不封装与封装的属性查找顺序
-
无封装时继承(通过谁实例化得到的对象就优先去谁里面找)
class Foo: def f1(self): print('Foo.f1') # 【四】在父类 Foo 里面找到了 f2 def f2(self): # 【五】打印 Foo.f2 print('Foo.f2') # 【六】self.f1 self.f1() class Bar(Foo): # 【七】因为是 通过Bar实例化得到的对象,所以 self 就是 Bar def f1(self): # 【八】打印 Bar.f1 print('Bar.f1') # 【三】Bar里面没有f2,去父类找 Foo # 【一】类实例化得到对象 b = Bar() # 【二】对象调用方法 f2 b.f2() # Foo.f2 # Bar.f1
-
有封装时继承(变形之后,就只能在当前自己的类中找)
class Foo: def __f1(self): print('Foo.f1') # 【四】在父类 Foo 里面找到了 f2 def f2(self): # 【五】打印 Foo.f2 print('Foo.f2') # 【六】self.__f1 # 没有变形时self 是谁就去谁里面找 # 变形之后,就只能在当前自己的类中找 self.__f1() class Bar(Foo): def __f1(self): # 【八】打印 Bar.f1 print('Bar.f1') # 【三】Bar里面没有f2,去父类找 Foo # 【一】类实例化得到对象 b = Bar() # 【二】对象调用方法 f2 b.f2() # Foo.f2 # Foo.f1
【小总结】
-
如果属性不封的情况下,谁实例化得到的self 就去谁里面找
-
如果属性封装的情况下 ,谁实例化得到的self 无效,只能在当前所在的类的名称空间里面找
【十】菱形结构继承顺序
-
在py2中分为深度优先和广度优先
-
在py3中只有广度优先
【1】深度优先
-
深度优先发生在经典类上,当前类内部找不到指定属性时,会向上找
-
一条线找到黑
【2】广度优先
-
广度优先发生在新式类上,在当前类内部找不到指定属性时,会向上找
-
顺序是广度优先
class A(object): def test(self): print('from A') class B(A): def test(self): print('from B') class C(A): def test(self): print('from C') class D(B): def test(self): print('from D') class E(C): def test(self): print('from E') class F(D, E): # def test(self): # print('from F') pass f1 = F() f1.test() # 新式类继承顺序:F->D->B->E->C->A # 经典类继承顺序:F->D->B->A->E->C
-
只有新式才有这个属性可以查看线性列表,经典类没有这个属性
print(F.__mro__)
【十一】派生
-
派生是指,子类继承父类,派生出自己的属性与方法,并且重用父类的属性与方法
【1】子类继承父类的属性
class People: school = '北大' def __init__(self,name,sex,age): self.name = name self.sex = sex self.age = age class Teacher(People): # 派生:派生出自己新的属性,在进行属性查找时,子类的属性会优先于父类被查找到 def __init__(self,name,sex,age,title): self.name = name self.sex = sex self.age = age self.title = title def teach(self): print('%s is teaching' % self.name) # # 只会找自己类中的__init__,并不会自动调用父类中的 obj = Teacher('knight','male',18,'教授') print(obj.teach(),obj.name,obj.sex,obj.age,obj.title,) # knight is teaching # None # knight male 18 教授
【2】继承方式一
class People: school = '清华大学' def __init__(self, name, sex, age): self.name = name self.sex = sex self.age = age class Teacher(People): # 派生:派生出自己新的属性,在进行属性查找时,子类的属性会优先于父类被查找到 def __init__(self,name,sex,age,title): # 直接调用 父类 中 的 __init__ 方法 # 调用的是函数,因而需要传入self People.__init__(self,name,sex,age) self.title = title def teach(self): print('%s is teaching' % self.name) # 只会找自己类中的__init__,并不会自动调用父类的 obj = Teacher('knight','male',18,'叫兽') print(obj.name,obj.sex,obj.age,obj.title)
【3】继承方式二
# 调用super()会得到一个特殊的对象 # 该对象专门用来引用父类的属性 # 且严格按照MRO规定的顺序向后查找 class People: school = '清华大学' def __init__(self, name, sex, age): self.name = name self.sex = sex self.age = age class Teacher(People): # 派生 : 派生出自己新的属性,在进行属性查找时,子类中的属性名会优先于父类被查找 def __init__(self, name, sex, age, title): # 直接调用 父类 中 的 __init__ 方法 # 调用的是绑定方法,因此会自动传入self,但是需要传入相应的参数 super().__init__(name, sex, age) self.title = title def teach(self): print('% is teaching' % self.name) # 只会找自己类中的__init__,并不会自动调用父类的 obj = Teacher('knight', 'male', 18, '叫兽') print(obj.name, obj.sex, obj.age, obj.title)
【十二】组合
在一个类中,以另外一个类的对象作为数据属性,称为类的组合
class Course: def __init__(self, name, period, price): self.name = name self.period = period self.price = price def tell_info(self): print(f'当前课程名字 {self.name} 当前课程周期 {self.period} 当前课程价格 {self.price}') class Date: def __init__(self, year, mon, day): self.year = year self.mon = mon self.day = day def tell_birth(self): print(f'当前生日 {self.year} 年 {self.mon} 月 {self.day} 日') class People: school = '清华大学' def __init__(self, name, sex, age,title): self.name = name self.sex = sex self.age = age self.title = title def tell_school_info(self): print(f"{self.name}在{self.school}工作") # Teacher类基于继承来重用People代码 # 基于组合来重用Date类和Course类的代码 class Teacher(People): def __init__(self, name, sex, age, title, year, mon, day): super().__init__(name, age, sex,title) # 老师有生日 self.birth = Date(year, mon, day) # 老师有课程,可以在实例化后,往该列表添加Course类的对象 self.courses = [] def teach(self): print(f"老师正在授课{self.name},目前是{self.title}") python = Course('python', '3mons', 3000) linux = Course('linux', '2mons', 2000) teacher1 = Teacher('knight', 'male', 18, 'NB', 2002, 6, 3) # teacher1有两门课程 teacher1.courses.append(python) teacher1.courses.append(linux) # 重用Data类的功能 teacher1.birth.tell_birth() teacher1.tell_school_info() teacher1.teach() # 重用Course类的功能 for obj in teacher1.courses: obj.tell_info() # 当前生日 2002 年 6 月 3 日 # knight在清华大学工作 # 老师正在授课knight,目前是NB # 当前课程名字 python 当前课程周期 3mons 当前课程价格 3000 # 当前课程名字 linux 当前课程周期 2mons 当前课程价格 2000
【十三】组合与继承的区别
-
组合与继承都是有效地利用已有类的资源的重要方式。但是二者的概念和使用场景皆不同
【1】继承的方式
-
通过继承建立了派生类与基类之间的关系,它是一种'是'的关系,比如白马是马,人是动物
-
当类之间有很多相同的功能,提取这些共同的功能做成基类,用继承比较好,比如老师是人,学生是人
【2】组合的方式
-
用组合的方式建立了类与组合的类之间的关系,它是一种‘有’的关系,比如教授有生日,教授教python和linux课程,教授有学生s1、s2、s3..