1、什么是继承?
继承指的是类与类之间的关系,是一种什么“是”什么的关系,继承的功能之一就是用来解决代码重用问题
继承是一种创建新类的方式,在python中,新建的类可以继承一个或多个父类
父类又可以称为基类或超类
新建的类称为派生类或子类
2、python中类的继承分为:单继承和多继承
class ParentClass1: #定义父类 pass class ParentClass2: #定义父类 pass class SubClass1(ParentClass1): #单继承,基类是ParentClass1,派生类是SubClass pass class SubClass2(ParentClass1,ParentClass2): #python支持多继承,用逗号分隔开多个继承的类 pass
print(SubClass1.__bases__)
print(SubClass2.__bases__)
结果:
(<class '__main__.ParentClass1'>,)
(<class '__main__.ParentClass1'>, <class '__main__.ParentClass2'>)
_bases_查看继承的父类,输出用元组表示
3、继承与抽象(先抽象再继承)
抽象即抽取类似或者说比较像的部分。
继承:是基于抽象的结果,通过编程语言去实现它,肯定是先经历抽象这个过程,才能通过继承的方式去表达出抽象的结构。
4、继承与重用性
在开发程序的过程中,如果我们定义了一个类A,然后又想新建立另外一个类B,但是类B的大部分内容与类A的相同时
我们不可能从头开始写一个类B,这就用到了类的继承的概念。
通过继承的方式新建类B,让B继承A,B会‘遗传’A的所有属性(数据属性和函数属性),实现代码重用
盖伦和瑞文都继承同一个父类(Hero)的属性
class Hero: def __init__(self,nickname,aggressivity,life_value): self.nickname = nickname self.aggressivity = aggressivity self.life_value = life_value def move_forward(self): print(f"move_forward{self.nickname}") def move_backward(self): print(f"move_backward{self.nickname}") def move_left(self): print(f"move_left{self.nickname}") def move_right(self): print(f"move_right{self.nickname}") def ack(self,enemy): enemy.life_value -= self.aggressivity class Garen(Hero): pass class Riven(Hero): pass hero = Garen('dema',30,200) hero2 = Riven('HAHA',50,150) print(hero2.life_value) hero.ack(hero2) print(hero2.life_value)
5、属性查找顺序,先从对象中查找,再到对象类中(实例属性中查找_init_,再到类属性查找),然后从父类中查找
提示:像上个例题中 hero.life_value之类的属性引用,会先从实例中找life_value然后去类中找
然后再去父类中找...直到最顶级的父类。那么如何解释下面的打印结果呢?
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')
b=Bar()# 定义对象 b.f2() # 实例化对象
结果:
Foo.f2
Bar.f1
6、派生
子类派生出自己新的属性(相对于继承的父类而言)
当然子类也可以添加自己新的属性或者在自己这里重新定义这些属性(不会影响到父类),需要注意的是,一旦重新定义了自己的属性且与父类重名,那么调用新增的属性时,就以自己为准了。
class Riven(Hero): camp='Noxus' def attack(self,enemy): #在自己这里定义新的attack,不再使用父类的attack,且不会影响父类 print('from riven') def fly(self): #在自己这里定义新的 print('%s is flying' %self.nickname)
7、继承的实现原理(多继承查找)C3算法
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中子类可以同时继承多个父类,
如果继承了多个父类,那么属性的查找方式有两种,分别是:深度优先和广度优先
7.1、python2中经典类,没有继承object的类,以及他的子类都称之为经典类
class Foo: pass class Bar(Foo): pass
7.2、python2中新式类,继承object的类,以及他的子类都称之为新式类
class Foo(object): pass class Bar(Foo): pass
7.3、python3,无论是否继承object,都默认继承object,均为新式类
class Foo:# 一个类没有继承object,默认继承object pass print(Foo.__bases__) 结果:(<class 'object'>,)
7.4、多继承下属性查找,经典类(深度优先),新式类(广度优先)
经典类:采取 深度优先 的方式查找,先一条道路走到底
新式类 广度优先 查找
# 多继承属性的查找,经典类(深度优先),新式类(广度优先) class A(object): def test(self): print('from A') class B(A): def test(self): print('from B') class C(A): def test(self): print('from C') class D(B): def test(self): print('from D') class E(C): def test(self): print('from E') class F(D,E): pass f1 = F() f1.test() print(F.__mro__) # 只有新式类才有这个属性可以查看线性列表,经典类没有这个属性,解析方法调用的顺序。
结果: from D (<class '__main__.F'>, <class '__main__.D'>, <class '__main__.B'>,
<class '__main__.E'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>)
若只保留A中的def test(self):查找到的结果如下:
class A(object): def test(self): print('from A') 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() print(F.__mro__) from A
(<class '__main__.F'>, <class '__main__.D'>, <class '__main__.B'>,
<class '__main__.E'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>)
8、在子类中调用父类的方法或属性(1、父类名.父类方法() ; 2、super())
在子类派生出的新方法中,往往需要重用父类的方法,我们有两种方式实现
方式一:指名道姓,即父类名.父类方法()
class Vehicle: #定义交通工具类 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) # 指名道姓的方式,不依赖继承 line13=Subway('中国地铁','180m/s','1000人/箱','电',13) line13.run()
结果:
地铁13号线欢迎您
开动啦...
方法二:super()
Vehicle.run(self)
方式二:super() class Vehicle: #定义交通工具类 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): #super(Subway,self) 就相当于实例本身 在python3中super()等同于super(Subway,self) super().__init__(name,speed,load,power)#父类的函数属性 self.line=line def run(self): print('地铁%s号线欢迎您' %self.line) super(Subway,self).run() # 等效于Vehicle.run(self)
class Mobike(Vehicle):#摩拜单车
pass
line13=Subway('中国地铁','180m/s','1000人/箱','电',13) line13.run()
这两种方式的区别是:方式一是跟继承没有关系的,而方式二的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())#__mro__给出了,即解析方法调用的顺序。
结果:
(<class '__main__.C'>, <class '__main__.A'>, <class '__main__.B'>, <class 'object'>)