python-类继承与重用
类的继承与重用
一、什么是继承?
1、继承是类与类间的关系。
2、是一种什么‘’是‘’什么的关系。
3、继承功能就是用来解决代码重用的问题。
1、继承: 是一种创建新类的方式,
python中,
1、新建的类可以继承一个或多个父类。
2、父类可以成为基类或超类。
3、所新建的类称为子类或派生类。
2、类继承的数量:
python中类的继承分为:继承一个或多个。
class ParentClass1: #定义父类 pass class ParentClass2: #定义父类 pass class SubClass1(ParentClass1): #单继承,基类是ParentClass1,派生类是SubClass pass class SubClass2(ParentClass1,ParentClass2): #python支持多继承,用逗号分隔开多个继承的类 pass
3、查看继承:
python中可以通过__base__命令可以查看
SubClass1.__bases__ #__base__只查看从左到右继承的第一个子类,__bases__则是查看所有继承的父类 #(<class '__main__.ParentClass1'>,) SubClass2.__bases__ #(<class '__main__.ParentClass1'>, <class '__main__.ParentClass2'>)
4、如果没有指定基类,python的类会默认继承object类,因为object类是所有python类的基类。
ParentClass1.__bases__ #由于ParentClass1这个类中没有继承类,python默认会继承object类,打印如下:
#(<class 'object'>,)
5、经典类与新式类,及其区别。
1、python2——才有经典与新式的区分。python3——统一都是新式类。
2、python2——没有显式继承object的类的,以及该类的子类,都是经典类。声明了object的类及子类,都是新式类。
3、python3——无论是否继承object,都默认继承object,因为python3所有的类都是新式类。
4、经典类与新式类的主要区别在:
经典类——多继承的情况,要查找的属性不存在时,按照深度优先。
新式类——。。。。。。。按照广度优先。
二、继承与抽象(先抽象再继承)
抽象——抽取类似或相似的部分。
继承——是抽象后的结果,通过编程来实现,一定要先有抽象的过程,才能有继承的结果。
抽象的过程——提取对象相似的特征构造成类,然后通过类,再抽象成这些类的父类。
继承的过程如下图。
三、继承与重用性
我们定义了一个类称A,再定义一个新类B,如果B这个类中有大部分的内容与A相同时。
我们就可以用到类的继承的概念。通过继承,B就会遗传A所有的属性(包括数据和函数),从而实现代码重用。
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('%s move forward' %self.nickname) def move_backward(self): print('%s move backward' %self.nickname) def move_left(self): print('%s move forward' %self.nickname) def move_right(self): print('%s move forward' %self.nickname) def attack(self,enemy): enemy.life_value-=self.aggressivity class Garen(Hero): pass class Riven(Hero): pass g1=Garen('草丛伦',100,300) r1=Riven('锐雯雯',57,200) print(g1.life_value) #结果:300 r1.attack(g1) print(g1.life_value) #结果:243
注意:1、用已有的类建立新的类,可大大节省代码。
2、 我们也可以继承被人的类如标准库,来定制新的类型。
四、属性查找
class Foo: def f1(self): print('Foo.f1') def f2(self): # 4、找到f2了执行命令 print('Foo.f2') self.f1() # 5、因为self 指的是对象本身,所以是b.f1() # 6、从bar()里找到f1() 所以打印Bar.f1 class Bar(Foo): # 3、在自己的类里面也没找到,所以在父类FOO里找 def f1(self): print('Bar.f1') b = Bar() # 2、从自己的类里面查找f1() b.f2() # 1、从对象b里查找f1(),由于没有定义__init__方法,所以找不到。
查找说明:
1、程序实例化了b=Bar()这个对象。
2、b.f2()这个方法在b 这个类里找不到,就找从FOO这个类里找.
3、在父类FOO中找到了f2()方法并执行。
4、当执行self.f1()时,由于self指的是类本身即Bar,所以执行Bar下的f1()。
五、派生
类可以继承父类的属性,也可以自己新属于自己的属性,一旦定义的属性名与父类的重名,调用的时候以自己的为准。
# 派生 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('%s move forward' % self.nickname) def move_backward(self): print('%s move backward' % self.nickname) def move_left(self): print('%s move forward' % self.nickname) def move_right(self): print('%s move forward' % self.nickname) def attack(self, enemy): enemy.life_value -= self.aggressivity class Riven(Hero): camp = 'Noxus' def attack(self, enemy): # 在自己这里定义新的attack,不再使用父类的attack,且不会影响父类 print('from riven', enemy) def fly(self): # 在自己这里定义新的 print('%s is flying' % self.nickname) r1 = Riven('锐雯雯', 57, 200) print(r1.attack(10))
注意:在子类中,如果新建了与父类重名的函数属性,在调用时也应该按照函数调用方式进行,即有多少个参数应传多少个值
六、继承的实现原理
Python实现继承的原理是在用户定义类的时候,计算并生成一个MRO线性列表。主要有如下原则:
1、子类优先于父类查找
2、多个父类会根据MRO列表顺序查找
3、如果下一个类对应两个合法选项,第一个父类优先。
注意:C和java只支持继承一个父类,python支持继承多个父类,如果继承多个父类,查找方式有两种:
经典类——深度优先:
新式类——广度优先
在python3查找顺序练习:
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): # def test(self): # print('from F') pass f1 = F() f1.test() # F D B E C A print(F.__mro__) # 只有新式才有这个属性可以查看线性列表,经典类没有这个属性 # 新式类继承顺序:F->D->B->E->C->A # 经典类继承顺序:F->D->B->A->E->C # python3中统一都是新式类 # pyhon2中才分新式类与经典类
由于py3的类统一是新式类,按照广度优先查找顺序,所以查找顺序为F=>D=>B=>E=>C=>A
如果是在py2,是经典类,按深度优先,所以是这样的F=>D=>B=>A=>E=>C
七、子类中调用父类的方法
有两种方法:
1、指名道姓调用
# 1、指名道姓方式引用,这种方式跟继承没有关系,只是简单的类的函数属性的引用 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()
注意:这样的调用方式,跟继承没有关系,相当于引用了类的函数属性,需要什么参数就要传什么参数。
2、super()方法调用
# 调用方法二:super() #A没有继承B,但是A内super会基于C.mro()继续往后找 class A: def test(self): print('test A') super().test() class B: def test(self): print('from B') class C(A,B): # print('test c') pass c=C() c.test() #打印结果:from B
注意:用super方法调用,是依赖于继承的,即使两个父类间没有继承关系,都会从子类的继承关系的MRO表继续往后找。