【一】继承介绍
# 面向对线三大特性:继承 封装 多态
# 继承
# 【一】什么是继承
# 继承就是一种创建新类的方式,新建的类可以继承一个或多个类的属性。
# 新的类如果有自己的属性,那就叫派生
# 【二】继承的优点
# 可以继承父类的所有属性和方法,实现代码的去重
# 【三】继承方式
# 单继承:继承一个父类的子类
# 多继承:继承多个父类的子类
# class Student(School):
# 继承的类叫父类 School
# 新建的类叫子类 Student
class Person(object):
height = 180
weight = 50
class School(object):
school = "清华"
# 【1】单继承:只继承一个父类
class Student(Person):
def __init__(self, name):
self.name = name
def 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 = name
def 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 = '狗'+name
cat_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 里面找到了 f2
def f2(self):
# 【五】打印 Foo.f2
print('Foo.f2')
# 【六】self.f1 ---> self 是谁 ?
# 触发的是 Foo.f1 还是 Bar.f1?
# Foo.f1 3
# Bar.f1 2
self.f1()
class Bar(Foo):
# 【七】因为是 通过Bar实例化得到的对象,所以 self 就是 Bar
def f1(self):
# 【八】打印 Bar.f1
print('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__f1
def __f1(self):
print('Foo.f1')
# 【4】在Foo里面找到了 f2
def f2(self):
# 【5】打印 Foo.f2
print('Foo.f2')
# 【6】
# 没有变形的时候 self 是谁就去谁里面找
# 但是变形之后 就只能在自己的类里面找,没办跳到别的类里面
# Foo.f1 2
# Bar.f1 3
self.__f1()
class Bar(Foo):
# 【3】没有f2. 去 Foo
def __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 = 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('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 = name
self.sex = sex
self.age = age
class Teacher(People):
# 派生 : 派生出自己新的属性,在进行属性查找时,子类中的属性名会优先于父类被查找
def __init__(self, name, sex, age, title):
# 直接调用 父类 中 的 __init__ 方法
# 调用的是函数,因而需要传入self
People.__init__(self, name, age, sex)
self.title = title
def 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 = 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('%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 = 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):
self.name = name
self.sex = sex
self.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...