【一】继承介绍
# 面向对线三大特性:继承 封装 多态
# 继承# 【一】什么是继承
# 继承就是一种创建新类的方式,新建的类可以继承一个或多个类的属性。
# 新的类如果有自己的属性,那就叫派生# 【二】继承的优点
# 可以继承父类的所有属性和方法,实现代码的去重# 【三】继承方式
# 单继承:继承一个父类的子类
# 多继承:继承多个父类的子类# class Student(School):
# 继承的类叫父类 School
# 新建的类叫子类 Studentclass Person(object):height = 180weight = 50class School(object):school = "清华"# 【1】单继承:只继承一个父类
class Student(Person):def __init__(self, name):self.name = namedef tell_me(self):print(f"我是 {self.name} 我身高 {self.height} 体重 {self.weight}")stu1 = Student("dream")
stu1.tell_me()# 【2】多继承:继承两个以上的父类
class Teacher(School, Person):def __init__(self, name):self.name = namedef tell_me(self):print(f"我是 {self.name} 我身高 {self.height} 体重 {self.weight} 学校在 {self.school}")teacher = Teacher("opp")
teacher.tell_me()# 【3】如何查看继承的父类
# (1)查看当前继承的父类 __base__ : 如果继承多个父类,默认只打印第一个继承的父类
print(Student.__base__) # <class '__main__.Person'>
print(Teacher.__base__) # <class '__main__.Person'>
# (2)查看当前继承的父类们 : __bases__ 一个元组,元组中放的是所有继承的父类
print(Student.__bases__) # (<class '__main__.Person'>,)
print(Teacher.__bases__) # (<class '__main__.Person'>, <class '__main__.School'>)
【二】经典类和新式类
# 经典类和新式类的区别在于Python版本的不同
# 在 py3 版本之前存在两个概念 ,在之后就没有经典类的概念了,只有新式类
# 【一】什么是经典类
# 在py2中没有显示继承 object 的类或者是该类的子类都是经典类
# 【二】什么是新式类
# 在py2中显示继承 object 的类或者是该类的子类都是新式类
# 在py3之后所有的类默认都是新式类,不写m默认继承 object
【三】继承和抽象
# 什么是继承和抽象
# 继承是子类和父类之间的关系,什么是什么的概念
# 抽象相当于将某部分抽立起来再总结# 【一】抽象
# 将不同的类。根据指定的表征总结起来归介于一个类
# 猫和狗 --> 动物
# 猪八戒 麦兜 ---> 猪
# 梅西 奥巴马 --> 人
# 猪 和 人 ---> 动物# 【二】继承
# 基于抽象的结果,然后用语言去实现# 【三】继承和抽象
# 继承是由少变多
# 抽象是由多变少# 【四】在python中实现继承
# 【1】没有继承和抽象
# 有一只猫
class Cat(object):def speak(self):print(f"猫可以喵喵叫")def eat(self):print("猫可以吃饭")def drink(self):print("猫可以喝水")# 有一只狗
class Dog(object):def speak(self):print(f"狗可以旺旺叫")def eat(self):print("狗可以吃饭")def drink(self):print("狗可以喝水")# 【2】抽象
# 总结起来一个公共的类,可以吃喝拉撒睡
class Animal(object):def speak(self):print(f"{self.name}可以叫")def eat(self):print(f"{self.name}可以吃饭")def drink(self):print(f"{self.name}可以喝水")# 【3】继承
# 有一只猫
class Cat(Animal):def __init__(self, name):self.name = '猫'+name# 有一只狗
class Dog(Animal):def __init__(self, name):self.name = '狗'+namecat_one = Cat(name='小花')
print(cat_one.speak())
dog_one = Dog(name="旺财")
print(dog_one.speak())
【四】封装后继承的属性查找顺序
【1】封装之前的属性查找顺序
class Foo:def f1(self):print('Foo.f1')# 【四】在父类 Foo 里面找到了 f2def f2(self):# 【五】打印 Foo.f2print('Foo.f2')# 【六】self.f1 ---> self 是谁 ?# 触发的是 Foo.f1 还是 Bar.f1?# Foo.f1 3# Bar.f1 2self.f1()class Bar(Foo):# 【七】因为是 通过Bar实例化得到的对象,所以 self 就是 Bardef f1(self):# 【八】打印 Bar.f1print('Bar.f1')# 【三】Bar里面没有f2,去父类找 Foo# 【一】类实例化得到对象
b = Bar()
# 【二】对象调用方法 f2
b.f2()
class B(object):def read(self):print(self)class A(B):...a = A()
a.read()b = B()
b.read()# 哪个对象调用方法,self 就是谁
【2】有封装的时候的继承
class Foo:# __f1 --> _Foo__f1def __f1(self):print('Foo.f1')# 【4】在Foo里面找到了 f2def f2(self):# 【5】打印 Foo.f2print('Foo.f2')# 【6】# 没有变形的时候 self 是谁就去谁里面找# 但是变形之后 就只能在自己的类里面找,没办跳到别的类里面# Foo.f1 2# Bar.f1 3self.__f1()class Bar(Foo):# 【3】没有f2. 去 Foodef __f1(self):print('Bar.f1')# 【1】
b = Bar()
# 【2】
b.f2()
【3】总结
# 如果属性不封装的情况下,谁实例化得到的self 就去谁里面找
# 如果属性封装的情况下 , 谁实例化得到的self 无效,只能在当前所在的类的民称空间里面找
【五】派生
# 派生是指,子类继承父类,派生出自己的属性与方法,并且重用父类的属性与方法
【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('dream', 'male', 18, '高级讲师')print(obj.name, obj.sex, obj.age, obj.title)
# dream 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, age, sex)self.title = titledef teach(self):print('%s is teaching' % self.name)# 只会找自己类中的__init__,并不会自动调用父类的
obj = Teacher('dream', 'male', 18, '高级讲师')print(obj.name, obj.sex, obj.age, obj.title)
# dream male 18 高级讲师
【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('%s is teaching' % self.name)# 只会找自己类中的__init__,并不会自动调用父类的
obj = Teacher('dream', 'male', 18, '高级讲师')print(obj.name, obj.sex, obj.age, obj.title)
# dream male 18 高级讲师
【六】组合
# 在一个类中以另外一个类的对象作为数据属性,称为类的组合。
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):self.name = nameself.sex = sexself.age = age# Teacher类基于继承来重用People的代码
# 基于组合来重用Date类和Course类的代码
class Teacher(People):# 老师是人def __init__(self, name, sex, age, title, year, mon, day):super().__init__(name, age, sex)# 老师有生日self.birth = Date(year, mon, day)# 老师有课程,可以在实例化后,往该列表中添加Course类的对象self.courses = []def teach(self):print(f'当前老师正在授课 {self.name}')python = Course('python', '3mons', 3000.0)
linux = Course('linux', '5mons', 5000.0)
teacher1 = Teacher('dream', 'male', 18, '金牌讲师', 1987, 3, 23)# teacher1有两门课程
teacher1.courses.append(python)
teacher1.courses.append(linux)# 重用Date类的功能
teacher1.birth.tell_birth()# 重用Course类的功能
for obj in teacher1.courses:obj.tell_info()# 当前生日 1987 年 3 月 23 日
# 当前课程名字 python 当前课程周期 3mons 当前课程价格 3000.0
# 当前课程名字 linux 当前课程周期 5mons 当前课程价格 5000.0
【六】组合和继承的区别
- 组合与继承都是有效地利用已有类的资源的重要方式。但是二者的概念和使用场景皆不同,
【1】继承的方式
- 通过继承建立了派生类与基类之间的关系,它是一种'是'的关系,比如白马是马,人是动物。
- 当类之间有很多相同的功能,提取这些共同的功能做成基类,用继承比较好,比如老师是人,学生是人
【2】组合的方式
- 用组合的方式建立了类与组合的类之间的关系,它是一种‘有’的关系,比如教授有生日,教授教python和linux课程,教授有学生s1、s2、s3...