1、什么是继承?
继承指的是类与类之间的关系,是一种什么“是”什么的关系,继承的功能之一就是用来解决代码重用问题
继承是一种创建新类的方式,在python中,新建的类可以继承一个或多个父类,父类又可以成为基类或超类,新建的类称为派生类或子类
-
# 父类基类超生类
-
# 子类派生类(继承父类)
-
# __bases__则是查看所有继承的父类
# 父类基类超生类 class ParentClass1: pass class ParentClass2: pass # 子类派生类(继承父类) class SubClass1(ParentClass1): pass class SubClass2(ParentClass1, ParentClass2): pass # __base__只查看从左到右继承的第一个子类, # __bases__则是查看所有继承的父类 print(SubClass1.__bases__) # 一层继承 (<class '__main__.ParentClass1'>,) print(SubClass2.__bases__)
2、继承与抽象(先抽象再继承)
抽象即抽取类似或者说比较像的部分。
抽象分成两个层次:
1.将奥巴马和梅西这俩对象比较像的部分抽取成类;
2.将人,猪,狗这三个类比较像的部分抽取成父类。
抽象最主要的作用是划分类别(可以隔离关注点,降低复杂度)
继承:是基于抽象的结果,通过编程语言去实现它,肯定是先经历抽象这个过程,才能通过继承的方式去表达出抽象的结构。
抽象只是分析和设计的过程中,一个动作或者说一种技巧,通过抽象可以得到类
3.继承与重用性 :人是动物
在开发程序的过程中,如果我们定义了一个类A,然后又想新建立另外一个类B,但是类B的大部分内容与类A的相同时
我们不可能从头开始写一个类B,这就用到了类的继承的概念。
通过继承的方式新建类B,让B继承A,B会‘遗传’A的所有属性(数据属性和函数属性),实现代码重用
class Hero: def __init__(self, nickname, life_value, aggresivity): self.nickname = nickname self.life_value = life_value self.aggresivity = aggresivity def attack(self, enemy): enemy.life_value -= self.aggresivity class Akl(Hero): city = 'Demacia' class Riven(Hero): city = 'Noxus' akl = Akl('暗影之拳', 100, 30) riven = Riven('锐萌萌', 100, 30) print(akl.life_value, akl.aggresivity, akl.nickname)
4、属性查找顺序
对象自己----> 自己所属的类-----> 自己的父类 # 没有的话报错
(2)属性,方法查找练习
提示:像g1.life_value之类的属性引用,会先从实例中找life_value然后去类中找,然后再去父类中找...直到最顶级的父类。那么如何解释下面的打印结果呢?
5、派生
当然子类也可以添加自己新的属性或者在自己这里重新定义这些属性(不会影响到父类),
需要注意的是,一旦重新定义了自己的属性且与父类重名,那么调用新增的属性时,就以自己为准了。
6、继承的实现原理
(1)方法解析顺序(MRO)列表
python到底是如何实现继承的,对于你定义的每一个类,python会计算出一个方法解析顺序(MRO)列表,这个MRO列表就是一个简单的所有基类的线性顺序列表,例如
>>> F.mro() #等同于F.__mro__ [<class '__main__.F'>, <class '__main__.D'>, <class '__main__.B'>, <class '__main__.E'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>]
为了实现继承,python会在MRO列表上从左到右开始查找基类,直到找到第一个匹配这个属性的类为止。而这个MRO列表的构造是通过一个C3线性化算法来实现的。我们不去深究这个算法的数学原理,它实际上就是合并所有父类的MRO列表并遵循如下三条准则:
- 子类会先于父类被检查
- 多个父类会根据它们在列表中的顺序被检查
- 如果对下一个类存在两个合法的选择,选择第一个父类
在Java和C#中子类只能继承一个父类,而Python中子类可以同时继承多个父类,如果继承了多个父类,那么属性的查找方式有两种,分别是:深度优先和广度优先
(2)新式类、经典类
# python2 # 经典类:没有继承object的类,以及它的子类都称之为经典类 class Parent: pass class Son(Parent): pass # 新式类:继承object的类,以及它的子类都称之为新式类 class Parent(object): pass class Son(Parent): pass
# python3 # 只有新式类:(<class 'object'>,)都是继承object类 # 一个类没有写object,也默认继承object类 class Foo: pass print(Foo.__bases__) class Foo2(object): pass print(Foo2.__bases__)
(3)广度优先
class E1: x = 'E1' class D1(E1): pass class D2(E1): # x = 'D2' pass class C1(D1): #x = 'C1' pass class C2(D2): # x = 'C2' pass class B1(C1): # x = 'B1' pass class B2(C2): # x = 'B2' pass class A(B1, B2): # x = 'A' pass a = A() print(A.__bases__) # 1层继承的父类 print(A.__mro__) # 所有继承的类, MRO列表就是一个简单的所有基类的线性顺序列表 print(a.x)
# 运行结果 (<class '__main__.B1'>, <class '__main__.B2'>) (<class '__main__.A'>, <class '__main__.B1'>, <class '__main__.C1'>, <class '__main__.D1'>, <class '__main__.B2'>, <class '__main__.C2'>, <class '__main__.D2'>,<class '__main__.E1'>, <class 'object'>) E1
(4)深度与广度区别
- 广度优先有些情况竟然和深度优先是相同的,让人疑惑??
# 深度优先 E D1 D2 C1 C2 B1 B2 A A--> B1-->C1--->D1-->E -->B2--->C2--->D2--->E # 广度优先1 E D1 D2 C1 C2 B1 B2 A (<class '__main__.A'>, <class '__main__.B1'>, <class '__main__.C1'>, <class '__main__.D1'>, <class '__main__.B2'>, <class '__main__.C2'>, <class '__main__.D2'>, <class '__main__.E'>, <class 'object'>) # 广度优先2 E1 E2 D1 D2 C1 C2 B1 B2 A (<class '__main__.A'>, <class '__main__.B1'>, <class '__main__.C1'>, <class '__main__.D1'>, <class '__main__.E1'>, <class '__main__.B2'>, <class '__main__.C2'>, <class '__main__.D2'>, <class '__main__.E2'>, <class 'object'>) # 广度优先3 E1 D1 D2 C1 C2 B1 B2 A (<class '__main__.A'>, <class '__main__.B1'>, <class '__main__.C1'>, <class '__main__.D1'>, <class '__main__.E1'>, <class '__main__.B2'>, <class '__main__.C2'>, <class '__main__.D2'>, <class 'object'>)
7、子类重用父类的属性、方法
在子类中调用父类的方法
在子类派生出的新方法中,往往需要重用父类的方法,我们有两种方式实现
- 方式一:指名道姓,即父类名.父类方法() (不依赖继承)
- 方式二:super() (依赖继承)
(1)为甚需要重用?
# 达到这种效果!!! class Hero: def __init__(self, nickname, life_value, aggresivity): self.nickname = nickname self.life_value = life_value self.aggresivity = aggresivity def attack(self, enemy): enemy.life_value -= self.aggresivity class Akl(Hero): city = 'Demacia' def __init__(self, nickname, life_value, aggresivity, weapon): self.nickname = nickname self.life_value = life_value self.aggresivity = aggresivity self.weapon = weapon class Riven(Hero): city = 'Noxus' akl = Akl('暗影之拳', 100, 30, 'knife') riven = Riven('锐萌萌', 100, 30) print(akl.__dic__)
(2) 属性重用:
- Hero.__init__(self,nickname, life_value, aggresivity,) # 指名道姓
class Hero: def __init__(self, nickname, life_value, aggresivity): self.nickname = nickname self.life_value = life_value self.aggresivity = aggresivity def attack(self, enemy): enemy.life_value -= self.aggresivity class Akl(Hero): city = 'Demacia' def __init__(self, nickname, life_value, aggresivity, weapon): # self.nickname = nickname # self.life_value = life_value # self.aggresivity = aggresivity Hero.__init__(self,nickname, life_value, aggresivity,) # 指名道姓 self.weapon = weapon class Riven(Hero): city = 'Noxus' akl = Akl('暗影之拳', 100, 30, 'knife') riven = Riven('锐萌萌', 100, 30) print(akl.__dict__)
- super(Akl, self).__init__(nickname, life_value, aggresivity,) # python2格式
- super().__init__(nickname, life_value, aggresivity,) # python3格式
属性、特征重用 class Hero: def __init__(self, nickname, life_value, aggresivity): self.nickname = nickname self.life_value = life_value self.aggresivity = aggresivity def attack(self, enemy): enemy.life_value -= self.aggresivity class Akl(Hero): city = 'Demacia' def __init__(self, nickname, life_value, aggresivity, weapon): # super(Akl, self).__init__(nickname, life_value, aggresivity,) # python2格式 super().__init__(nickname, life_value, aggresivity,) # python3格式 self.weapon = weapon class Riven(Hero): city = 'Noxus' akl = Akl('暗影之拳', 100, 30, 'knife') riven = Riven('锐萌萌', 100, 30) print(akl.__dict__)
(3) 方法重用:
- Hero.attack(self, enemy) # 指名道姓
#达到这种效果!!! class Hero: def __init__(self, nickname, life_value, aggresivity): self.nickname = nickname self.life_value = life_value self.aggresivity = aggresivity def attack(self, enemy): enemy.life_value -= self.aggresivity class Akl(Hero): city = 'Demacia' def attack(self, enemy): Hero.attack(self, enemy) # 指名道姓 print('form akl class') class Riven(Hero): city = 'Noxus' akl = Akl('暗影之拳', 100, 30) riven = Riven('锐萌萌', 100, 30) print(akl.__dict__) akl.attack(riven) print(riven.__dict__)
-
super(Akl, self).attack(enemy) # 依赖继承
# 技能方法重用 class Hero: def __init__(self, nickname, life_value, aggresivity): self.nickname = nickname self.life_value = life_value self.aggresivity = aggresivity def attack(self, enemy): enemy.life_value -= self.aggresivity class Akl(Hero): city = 'Demacia' def attack(self, enemy): # Hero.attack(self, enemy) # 指名道姓 # super(Akl, self).attack(enemy) # 依赖继承 super().attack(enemy) print('form akl class') class Riven(Hero): city = 'Noxus' akl = Akl('暗影之拳', 100, 30) riven = Riven('锐萌萌', 100, 30) print(akl.__dict__) akl.attack(riven) print(riven.__dict__)
(4)派生与重用的区别
- 派生:用的是父类的方法名,不适用父类方法的功能,定义新的功能
- 重用:用的是父类的方法名,和功能,也添加了新的功能
8、super()依赖继承
方式二的super()是依赖于继承的, 并且即使没有直接继承关系,
super仍然会按照mro继续往后查找
(<class '__main__.A'>, <class '__main__.B1'>, <class '__main__.B2'>, <class 'object'>) from B1 form B2
class C1: def fun1(self): print('form c1') super().fun1() # 基于最底下子类的mor顺序查找 class C2: def fun1(self): print('form c2') class B2(C2): def fun1(self): print('form B2') class B1(C1): def fun1(self): print('from B1') super().fun1() class A(B1, B2): pass print(A.__mro__) a = A() a.fun1()
(<class '__main__.A'>, <class '__main__.B1'>, <class '__main__.C1'>, <class '__main__.B2'>, <class '__main__.C2'>, <class 'object'>) from B1 form c1 form B2