day 20
1. 类的继承
1.1 什么是继承
- 继承是一种新建类的方式,新建的类称为子类,被继承的类称为父类
- 继承的特性是:子类会遗传父类的属性,并且可以派生出自己的属性
- 继承是类与类之间的关系
在 python 中,一个子类可以继承多个父类,其他语言只能一个子类继承一个父类。
1.2 为什么要有继承
目的:减少代码冗余(减少重复代码)
1.3 如何实现继承
-
先确定谁是子类,谁是父类
-
在定义类时,子类 + (),括号内填父类的名字,实现继承
使用
__bases__
方法,可以获取当前类所继承的类class Father1: pass class Father2: pass class Son1(Father1): pass class Son2(Father1, Father2): pass print(Son1.__bases__) # 查看父类 print(Son2.__bases__)
运行结果:
(<class '__main__.Father1'>,) (<class '__main__.Father1'>, <class '__main__.Father2'>) Process finished with exit code 0
1.4 类与抽象
- 继承的关系
- 对象是特征与技能的结合体
- 类是一系列对象相同的特征与技能的结合体
- 继承是一系列类相同的特征与技能的结合体
继承描述的是子类与父类之间的关系,是一种什么是什么的关系。要找出这种关系,必须先抽象再继承,抽象即抽取类似或者说比较像的部分。
抽象分成两个层次:
- 将奥巴马和梅西这俩对象比较像的部分抽取成类;
- 将人,猪,狗这三个类比较像的部分抽取成父类。
抽象最主要的作用是划分类别(可以隔离关注点,降低复杂度),如下图所示:
继承:基于抽象的结果,通过编程语言去实现它,肯定是先经历抽象这个过程,才能通过继承的方式去表达出抽象的结构。
抽象只是分析和设计的过程中,一个动作或者说一种技巧,通过抽象可以得到类,如下图所示:
1.5 查找顺序
在继承背景下,对象属性的查找顺序:
- 对象查找属性会先从对象的名称空间中查找
- 若对象没有,则会去类里面找
- 若当前类是子类,并且没有对象找的属性,会去父类中查找
注意:对象查找属性,若子类有,不管父类有没有,以子类的为准。
class Foo:
def f1(self):
print('Foo.f1')
def f2(self):
print('Foo.f2')
self.f1()
class Bar(Foo):
def f1(self):
print('Bar.f1')
# 对象查找属性的顺序:对象自己-》对象的类-》父类-》父类。。。
obj = Bar() # self是obj本身,即找到Bar的f1()
obj.f2()
运行结果:
Foo.f2
Bar.f1
Process finished with exit code 0
2. 派生
2.1 什么是派生
子类中新定义的属性的这个过程叫做派生,并且需要记住子类在使用派生的属性时始终以自己的为准。
2.2 派生方法
2.2.1 类调用
指名道姓访问某一个类的函数:该方式与继承无关
# 直接通过 父类.(调用)__init__,把__init__当做普通函数使用,传入对象与继承的属性.
class Father:
def __init__(self, name, age):
self.name = name
self.age = age
class Son(Father):
def __init__(self, name, age, weight, height):
Father.__init__(self, name, age)
self.weight = weight
self.height = height
2.2.2 super()
方法
- 严格依赖继承属性查找关系
super()
会得到一个特殊的对象,该对象就是专门用来访问父类中的属性的(按照继承的关系)super().__init__
(不用为self传值)super()
的完整用法是super(自己的类名,self)
,在python2中需要写完整,而python3中可以简写为super()
class Father:
def __init__(self, name, age):
self.name = name
self.age = age
class Son(Father):
def __init__(self, name, age, weight, height):
super().__init__(name, age)
self.weight = weight
self.height = height
3. 类的分类(了解)
在 python2 中,才有才会有新式类与经典类之分;在 python3 中,所有的类都是新式类。
3.1 新式类
继承 object 的类都称之为新式类。
在 python3 中,如果子类没有继承自定义的类,都默认继承 object 。
class Foo():
pass
class Goo(Foo):
pass
print(Foo.__bases__)
print(Goo.__bases__)
运行结果:
(<class 'object'>,) # 默认继承 object
(<class '__main__.Foo'>,)
Process finished with exit code 0
3.2 经典类
在 python2 中,凡是没有继承 object 的类都是经典类。
4. 菱形继承问题
4.1 菱形继承问题
在Java和C#中子类只能继承一个父类,而Python中子类可以同时继承多个父类,如A(B,C,D)
如果继承关系为非菱形结构,则会按照先找B这一条分支,然后再找C这一条分支,最后找D这一条分支的顺序直到找到我们想要的属性
如果继承关系为菱形结构,即子类的父类最后继承了同一个类,那么属性的查找方式有两种:
- 经典类下:深度优先
- 广度优先:广度优先
经典类:一条路走到黑,深度优先
新式类:不找多各类最后继承的同一个类,直接去找下一个父类,广度优先
可以使用下面的代码逐一进行验证:
# 验证
class A:
def test(self):
print('from A')
pass
class B(A):
def test(self):
print('from B')
pass
class C(A):
def test(self):
print('from C')
pass
class D(B):
def test(self):
print('from D')
pass
class E(C):
def test(self):
print('from E')
pass
class F(D, E):
def test(self):
print('from F')
pass
f1 = F()
f1.test()
结果:
# 新式类: F-D-B-E-C-A-object
# 经典类: F-D-B-A-E-C
4.2 mro()
方法
对于多继承的类,python 提供了 mro()
方法,便捷的查看继承的查找顺序。
对于上述代码,我们可以使用 mro()
方法:
# print(F.mro())
# 新式类下:
for i in F.mro():
print(i)
运行结果:
<class '__main__.F'>
<class '__main__.D'>
<class '__main__.B'>
<class '__main__.E'>
<class '__main__.C'>
<class '__main__.A'>
<class 'object'>
Process finished with exit code 0