【一】继承
1)概念
-
继承是一种创建新类的方式,新建的类可以继承一个或多个类的属性
-
可以继承父类的所有属性和方法,实现代码的去重
class Student(School):
# 继承的类叫父类 School
# 新建的类叫子类 Student
2)单继承
class Person(object):
height = 170
weight = 70
class Student(Person):
def __init__(self, name):
self.name = name
def eg(self):
print(f'{self.name}身高{self.height}体重{self.weight}')
st1 = Student('diva')
st1.eg()
3)多继承
class Height(object):
height = 170
class Weight(object):
weight = 70
class Student(Height, Weight):
def __init__(self, name):
self.name = name
def eg(self):
print(f'{self.name}身高{self.height}体重{self.weight}')
st1 = Student('diva')
st1.eg()
4)查看继承的父类
__base__:只打印一个继承的父类
__bases__:元组中放入所有继承的父类
【二】经典类和新式类
-
经典类
- 在python2中,没有显式的继承object类的类,以及该类的子类,都是经典类
-
新式类
- 在python2中,显式地声明继承object的类,以及该类的子类,都是新式类
在python3中,无论是否继承object,都默认继承object,即python3中所有类均为新式类
【三】继承与抽象
-
继承
- 是子类与父类之间的关系,是一种什么是什么的概念
-
抽象
- 抽取类似或比较像的部分
- 主要作用是划分类别
【四】属性查找顺序
1)不隐藏属性
class Foo:
def f1(self):
print('Foo.f1')
#【2】找到f2,执行
def f2(self):
print('Foo.f2')
#【3】self 是谁就去谁里面找
self.f1()
#【1】从Bar未找到f2,就去父类Foo找
class Bar(Foo):
#【4】因为是通过Bar实例化得到的对象,所以执行Bar的f1
def f1(self):
print('Bar.f1')
b = Bar()
b.f2()
# Foo.f2
# Bar.f1
2)隐藏属性
class Foo:
#【4】 __f1 --> _Foo__f1
def __f1(self):
print('Foo.f1')
#【2】找到f2,执行
def f2(self):
print('Foo.f2')
#【3】变形之后就只能在自己的类里面找,没办跳到别的类里面
self.__f1()
#【1】从Bar未找到f2,就去父类Foo找
class Bar(Foo):
def __f1(self):
print('Bar.f1')
b = Bar()
b.f2()
# Foo.f2
# Foo.f1
3)总结
如果属性不封装的情况下,谁实例化得到的self 就去谁里面找
如果属性封装的情况下 ,只能在当前所在的类的民称空间里面找
【五】菱形继承问题
1)深度优先
- 当类是经典类时,多继承情况下,在查找属性不存在时,会按照深度优先的方式查找下去
2)广度优先
- 当类是新式类时,多继承情况下,在查找属性不存在时,会按照广度优先的方式查找下去
【六】派生
子类继承父类,派生出自己的属性与方法,并且重用父类的属性与方法
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(f'{self.name} is teaching')
# 只会找自己类中的__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(f'{self.name} is teaching')
obj = Teacher('dream', 'male', 18, '高级讲师')
print(obj.name, obj.sex, obj.age, obj.title)
# dream male 18 高级讲师
3)超类(super())
- 调用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(f'{self.name} is teaching')
obj = Teacher('dream', 'male', 18, '高级讲师')
print(obj.name, obj.sex, obj.age, obj.title)
# dream male 18 高级讲师
【六】组合与继承的区别
- 组合
- 在一个类中以另外一个类的对象作为数据属性
- 用组合的方式建立了类与组合的类之间的关系,它是一种‘有’的关系
- 继承
- 通过继承建立了派生类与基类之间的关系,它是一种'是'的关系