Python3.7之继承
一、什么叫继承
继承是一种创建新类的方式,新建的类可以继承一个或多个父类(python支持多继承),父类又可称为基类或超
类,新建的类称为派生类或子类。子类会“”遗传”父类的属性,从而解决代码重用问题。
单继承与多继承
class Parent1:
pass
class Parent2:
pass
class Children1(Parent1):
pass
class Children2(Parent1, Parent2):
pass
print(Children2.__bases__)
print(Children1.__bases__)
'''
(<class '__main__.Parent1'>, <class '__main__.Parent2'>)
(<class '__main__.Parent1'>,)
'''
经典类与新式类
只有在python2中才分新式类和经典类,python3中统一都是新式类
在python2中,没有显式的继承object类的类,以及该类的子类,都是经典类
在python2中,显式地声明继承object的类,以及该类的子类,都是新式类
在python3中,无论是否继承object,都默认继承object,即python3中所有类均为新式
二、继承与抽象(先抽象再继承)
抽象即抽取类似或者说比较像的部分。
抽象最主要的作用是划分类别(可以隔离关注点,降低复杂度)
继承:是基于抽象的结果,通过编程语言去实现它,肯定是先经历抽象这个过程,才能通过继承的方式去表达出抽象的结构。
抽象只是分析和设计的过程中,一个动作或者说一种技巧,通过抽象可以得到类
三、继承与重用性
在开发程序的过程中,如果我们定义了一个类A,然后又想新建立另外一个类B,但是类B的大部分内容与类A的相
同时,我们不可能从头开始写一个类B,这就用到了类的继承的概念。
通过继承的方式新建类B,让B继承A,B会‘遗传’A的所有属性(数据属性和函数属性),实现代码重用
class Hero:
def __init__(self, nickname, attack_value, life_value):
self.nickname = nickname
self.attack_value = attack_value
self.life_value = life_value
def attack(self, enemy):
enemy.life_value -= self.attack_value
class Garen(Hero):
camp = 'Demacia'
class Riven(Hero):
camp = 'Noxus'
role1 = Garen('A', 50, 100)
role2 = Riven('B', 30, 80)
print(role1.camp)
print(role2.camp)
role1.attack(role2)
print(role2.life_value)
属性查找顺序
先从实例中找,然后去类中找,然后再去父类中找...直到最顶级的父类。
class Foo:
def f1(self):
print('f1 From Foo')
def f2(self):
print('f2 from Foo')
self.f1() # b.f1,此时self为实例b
class Bar(Foo):
def f1(self):
print('f1 from Bar')
b = Bar()
b.f2()
'''
f2 from Foo
f1 from Bar
'''
方法解析顺序(mro)
四、继承的实现原理
Python中子类可以同时继承多个父类,如果继承了多个父类,那么属性的查找方式有两种,分别是:深度优先和
广度优先。
我们不去深究这个算法的数学原理,它实际上就是合并所有父类的MRO列表并遵循如下三条准则:
1.子类会先于父类被检查
2.多个父类会根据它们在列表中的顺序被检查
3.如果对下一个类存在两个合法的选择,选择第一个父类
当类是经典类时,多继承情况下,在要查找的属性不存在时,会按照深度优先的方式查找下去;
当类是新式类时,多继承情况下,在要查找的属性不存在时,会按照广度优先的方式查找下去。
五、在子类中调用父类的方法
1.父类名.父类方法()
非绑定方法调用
class Garen(Hero):
def __init__(self, nickname, attack_value, life_value, skin):
Hero.__init__(self, nickname, attack_value, life_value)
# 调用父类功能,注意这里必须要有self
self.skin = skin # 新属性
def attack(self, enemy):
Hero.attack(self, enemy) # 调用父类功能,参数必须与父类一致
print('From Garen') # 自己新的功能
camp = 'Demacia'
2. super()
builtin函数super
class Hero:
camp = 'Hero'
def __init__(self, nickname, attack_value, life_value):
self.nickname = nickname
self.attack_value = attack_value
self.life_value = life_value
def attack(self, enemy):
enemy.life_value -= self.attack_value
class Garen(Hero):
def __init__(self, nickname, attack_value, life_value, skin):
# super(Garen,self) 就相当于实例本身 在python3中super()等同于super(Garen,self)
super().__init__(nickname, attack_value, life_value)
self.skin = skin
def attack(self, enemy):
super().attack(enemy)
print('From Garen')
camp = 'Demacia'
class Riven(Hero):
camp = 'Noxus'
role1 = Garen('A', 50, 100, 'A1')
role2 = Riven('B', 30, 80)
print(role1.skin)
role1.attack(role2)
print(role2.life_value)
这两种方式的区别是:方式一是跟继承没有关系的,而方式二的super()是依赖于继承的,并且即使没有直接继承
关系,super仍然会按照mro继续往后查找。
# A没有继承B,但是A内super会基于C.mro()继续往后找
class A:
def test(self):
super().test()
class B:
def test(self):
print('from B')
class C(A, B):
pass
c = C()
c.test() # 打印结果:from B
print(C.mro())
'''
from B
[<class '__main__.C'>, <class '__main__.A'>, <class '__main__.B'>, <class 'object'>]
'''
四、继承的好处
提高代码的复用。
五、继承的弊端
可能特殊的本类又有其他特殊的地方,又会定义一个类,其下也可能再定义类,这样就会造成继承的那条线越来越
长,使用继承的话,任何一点小的变化也需要重新定义一个类,很容易引起类的爆炸式增长,产生一大堆有着细微不同
的子类. 所以有个“多用组合少用继承”的原则。