004---继承与派生
初识继承
什么是继承
- 继承指的是类与类之间的关系,是一种什么是什么的关系。
- 继承是用来创建新类的一种方式。在python中,新建的类可以继承一个或多个父类。
- 父类又叫做基类或超类、新建的类又叫做子类或派生类
种类:多继承和单继承
class P1: # 定义父类P1
pass
class P2: # 定义父类P2
pass
class P_chirdren1(P1): # 单继承:基类是P1,派生类是P_chirdren1
pass
class P_chirdren2(P1, P2):# 多继承:基类是P1、P2, 派生类是P_chirdren2
pass
查看继承
# __base__只查看从左到右继承的第一个父类
# __bases__则是查看子类继承的所有父类
print(P_chirdren1.__base__) # class '__main__.P1'
print(P_chirdren2.__base__) # class '__main__.P1'
print(P_chirdren2.__bases__) # (class '__main__.P1', class '__main__.P2')
新式类和经典类
- 在python2当中
- 经典类:没有继承object,以及他的子类都是经典类
- 新式类:继承了object,以及她的子类都是新式类
- 在python3当中
- 无论是否继承object,都默认继承object,即python3中所有类都是新式类
继承和抽象(先抽象和继承)
抽象就是把多个类相似的特征和行为抽取出来,抽取到父类,然后继承它。
- 继承:是基于抽象的结果
- 抽象:只是分析和设计的过程中,一个动作或技巧,通过抽象可以得到类。
派生
子类继承了父类的属性和方法,当然也可以添加自己新的属性和方法或者重写,不会影响到父类。但是调用的时候就会以自己的为准,不会调用父类的。
class Hero:
"""
英雄类
"""
def __init__(self, nickname, life_value, aggresivity):
self.nickname = nickname
self.life_value = life_value
self.aggresivity = aggresivity
def acctack(self, enemy):
enemy.life_value -= self.aggresivity
if enemy.life_value < 0:
print('%s 取得胜利,杀死了%s' % (self.nickname, enemy.nickname))
class Garen(Hero):
"""
盖伦类
"""
# 声明一个新属性
camp = 'Demacia'
def acctack(self):
"""
重写父类当中的acctack()。
:return:
"""
print('from Garen')
class Riven(Hero):
"""
瑞文类
"""
camp = 'Noxus'
g1 = Garen('葛小伦', 21, 30)
print(g1.camp) # Demacia
g1.acctack() # from Garen
继承的实现原理
对于定义的每一个类。python会计算出一个方法解析顺序列表(mro),它代表了类继承的顺序,也代表了子类的属性和方法的查找顺序。
class A:
def test(self):
print('A')
class B(A):
def test(self):
print('B')
class C(A):
def test(self):
print('C')
class D(B):
def test(self):
print('D')
class E(C):
def test(self):
print('E')
class F(D, E):
def test(self):
# print('D')
pass
# 新式类 D-->B-->E-->C-->A
print(F.mro()) # (class '__main__.F', class '__main__.D', class '__main__.B', class '__main__.E', class '__main__.C', class '__main__.A', class 'object')
print(F.__mro__) # [class '__main__.F', class '__main__.D', class '__main__.B', class '__main__.E', class '__main__.C', class '__main__.A', class 'object']
- mro:Method Resolution Order,即方法解析顺序,是python处理二义性问题的算法,我们不需要关心算法内部怎么实现。只要知道属性的查找方式有两种:深度优先和广度优先。
- 注意:只有新式类才有mro这个属性,经典类没有。
在子类中调用父类的方法
- 指名道姓(不依赖继承):父类.父类method(self,*args,**kwargs)
class Vehicle(object):
"""
交通工具类
"""
country = 'China'
def __init__(self, name, speed, load, power):
self.name = name
self.speed = speed
self.load = load
self.power = power
def run(self):
print('开动了')
class Subway(Vehicle):
"""
地铁类
"""
def __init__(self, name, speed, load, power, line):
# 调用父类的初始化方法
Vehicle.__init__(self, name, speed, load, power)
self.line = line
def run(self):
print('地铁%s欢迎你' % self.line)
Vehicle.run(self)
s = Subway('北京','180km/h','100人/节','电','1号线')
s.run()
- super(依赖继承)
class Vehicle(object):
"""
交通工具类
"""
country = 'China'
def __init__(self, name, speed, load, power):
self.name = name
self.speed = speed
self.load = load
self.power = power
def run(self):
print('开动了')
class Subway(Vehicle):
"""
地铁类
"""
def __init__(self, name, speed, load, power, line):
# 相当于实例本身,在python中:super() == super(Subway,self)
# super(Subway, self).__init__(name,speed,load,power)
super().__init__(name,speed,load,power)
self.line = line
def run(self):
print('地铁%s欢迎你' % self.line)
# super(Subway, self).run()
super().run()
s = Subway('北京','180km/h','100人/节','电','1号线')
s.run()
- 需要注意的是,在多继承的情况下。super并不是一味地只找父类。而是按照子类的mro顺序去查找。
# 误区
class A:
def f1(self):
print('A')
super().f1()
class B:
def f1(self):
print('B')
class C(A, B):
pass
c = C()
c.f1() # A B super会让人觉得他继承了B,就去B类寻找f1方法。 实际上,super依赖继承,根据c的mRo列表一步步找。
print(C.__mro__) # (class '__main__.C', class '__main__.A', class '__main__.B', class 'object')
继承的优点
- 最大的优点:解决了代码冗余,代码重用,节省了代码