【一】什么是继承
-
继承就是创建新类的一种方式,这个新类可以继承一个或者多个其他类的属性
-
新的类如果有自己属性,那就叫派生
【二】继承的优点
-
可以继承父类的所有属性和方法,这样就可以实现代码去重。
【三】继承方式
-
单继承:继承一个父类的子类
-
多继承:继承多个父类的子类
例如:
class Student(School): # 被继承的类叫父类 School # 新类就叫子类 Student
【四】单继承
class Person(object):height = 180weight = 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 = 180weight = 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 里面找到了 f2def f2(self):# 【五】打印 Foo.f2print('Foo.f2')# 【六】self.f1self.f1() class Bar(Foo):# 【七】因为是 通过Bar实例化得到的对象,所以 self 就是 Bardef f1(self):# 【八】打印 Bar.f1print('Bar.f1')# 【三】Bar里面没有f2,去父类找 Foo # 【一】类实例化得到对象 b = Bar() # 【二】对象调用方法 f2 b.f2() # Foo.f2 # Bar.f1
-
有封装时继承(变形之后,就只能在当前自己的类中找)
class Foo:def __f1(self):print('Foo.f1')# 【四】在父类 Foo 里面找到了 f2def f2(self):# 【五】打印 Foo.f2print('Foo.f2')# 【六】self.__f1# 没有变形时self 是谁就去谁里面找# 变形之后,就只能在当前自己的类中找self.__f1()class Bar(Foo):def __f1(self):# 【八】打印 Bar.f1print('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')passf1 = 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 = nameself.sex = sexself.age = ageclass Teacher(People):# 派生:派生出自己新的属性,在进行属性查找时,子类的属性会优先于父类被查找到def __init__(self,name,sex,age,title):self.name = nameself.sex = sexself.age = ageself.title = titledef 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 = nameself.sex = sexself.age = ageclass Teacher(People):# 派生:派生出自己新的属性,在进行属性查找时,子类的属性会优先于父类被查找到def __init__(self,name,sex,age,title):# 直接调用 父类 中 的 __init__ 方法# 调用的是函数,因而需要传入selfPeople.__init__(self,name,sex,age)self.title = titledef 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 = nameself.sex = sexself.age = ageclass Teacher(People):# 派生 : 派生出自己新的属性,在进行属性查找时,子类中的属性名会优先于父类被查找def __init__(self, name, sex, age, title):# 直接调用 父类 中 的 __init__ 方法# 调用的是绑定方法,因此会自动传入self,但是需要传入相应的参数super().__init__(name, sex, age)self.title = titledef 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 = nameself.period = periodself.price = pricedef tell_info(self):print(f'当前课程名字 {self.name} 当前课程周期 {self.period} 当前课程价格 {self.price}')class Date:def __init__(self, year, mon, day):self.year = yearself.mon = monself.day = daydef tell_birth(self):print(f'当前生日 {self.year} 年 {self.mon} 月 {self.day} 日')class People:school = '清华大学'def __init__(self, name, sex, age,title):self.name = nameself.sex = sexself.age = ageself.title = titledef 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..